Compilers - Formal Grammar
Compilers - Formal Grammar
自顶向下
- 的两个动作:匹配,推导(展开)。
- 从开始符出发,推导出给定的串。
Terminologies
- 导空:\(X\) 可导空意味着 \(X \Rightarrow^* \varepsilon\)。
FIRST set
FIRST set (FIRST 集) 是由给定串能推导到的所有第一个终结符的集合。
Derive the FIRST set according to its definition
- 对于推导规则 \(S \to X_0 X_1 X_2 ... X_n\),找到第一个不能导空的符号(记为 \(j\)),则令 \(\text{FIRST}(S) := \text{FIRST}(X_0) \cup \text{FIRST}(X_1) \cup \dots \cup \{X_j\} - \{\varepsilon\}\)。
- 若此串能推导为空,则集合加上 \(\varepsilon\)。
FOLLOW set
FOLLOW set (FOLLOW 集)是由给定非终结符后可能出现的所有终结符的集合。
Derive the FOLLOW set according to its definition
- 对开始符 \(S\),\(\text{FOLLOW}(S) := \{\#\}\);对于其他符号(如 \(A\)),\(\text{FOLLOW}(A) := \{\}\)。
- 要求 \(\text{FOLLOW}(E)\),需找到所有 \(E\) 在右边的推导规则,不妨记为 \(A \Rightarrow \alpha E \beta\)。 则令 \(\text{FOLLOW}(E) := \text{FOLLOW}(E) \cup \text{FIRST}(\beta)\)。 若 \(\beta \Rightarrow^* \varepsilon\),则令 \(\text{FOLLOW}(E) := \text{FOLLOW}(E) \cup \text{FOLLOW}(A)\)。
- 去掉集合中的 \(\varepsilon\)。
SELECT set
SELECT set (SELECT 集) 描述了“应当在什么输入符号下选择某条产生式”。
即:若
\[X \in \text{SELECT}(A \to \alpha),\]则当下一个符号输入为 \(X\) 时,可选择产生式 \(A \to \alpha\)。
形式化地:
\[\text{SELECT}(A \to \alpha) = \begin{cases} \text{FIRST}(\alpha), & \text{if } \varepsilon \notin \text{FIRST}(\alpha) \\[6pt] (\text{FIRST}(\alpha) - \{\varepsilon\}) \cup \text{FOLLOW}(A), & \text{if } \varepsilon \in \text{FIRST}(\alpha) \end{cases}\]LL grammar
Definition: see https://en.wikipedia.org/wiki/LL_grammar.
对于 \(\text{LL}(k)\) :
- \(\text{LL}\): From Left to right, derive Leftmost word.
- \(k\): 选择产生式时,需向前看 k 个字符。
LL(1) grammar
特别地,对 \(\text{LL}(1)\) 来说,对任意的非终结符 \(A\),SELECT 集之间不能有交集。
Determine whether a grammar is LL(1) and convert non-LL(1) to LL(1)
以下步骤均为 \(\text{LL}(1)\) 型文法的必要条件,而非充分条件。
提取左公共因子
给定
\[A \to \alpha \beta \mid \alpha \gamma,\]其中右边有公共左前缀,则将其提取出来:
\[\begin{align} A &\to \alpha A' \\ A' &\to \beta \mid \gamma \\ \end{align}\]为防止隐含/间接公共左因子,需展开产生式。具体方法见:处理隐含因子。
消除左递归
- 左递归形式: \(A \to A \alpha \mid \beta\)
- 右递归形式:\(A \to \alpha A \mid \beta\)
- 递归形式(既不算左递归也不算右递归):\(A \to \alpha A \beta \mid \gamma\)
找到所有左递归,并将其转化为右递归:
\[\begin{align} A &\to \beta A' \\ A' &\to \varepsilon \mid \alpha A' \\ \end{align}\]若产生式不是标准左递归形式,但存在隐式左递归,应首先将其转化为标准形式。
为防止隐含/间接左递归,需展开产生式。具体方法见:处理隐含因子。
隐含/间接 公共左因子/左递归
LL(1) 分析表
将 SELECT 集转化为 LL(1) 分析表,即一个从产生式左边到文本串开头符号的映射。
形如:
| Current\Next | a | b | c |
|---|---|---|---|
| X | \(X\to\beta\) | … | |
| Y | … | ||
| Z |
示例 C++ 实现,见 GitHub compilers-labs:
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
bool Grammar::match(Symbol_string s) const
{
assert(std::ranges::all_of(
s, [this](auto const &s) { return terminals_.contains(s); }));
s.push_back(eol);
Symbol_string t{eol, begin_}; // Acts as a stack
auto ll1t = ll1_parse_table();
while (!t.empty()) {
// std::println("t={} {}=s", t, s);
if (terminals_.contains(t.back()) || t.back() == eol) {
if (t.back() != s.front()) {
// std::println("Terminal doesn't match, failed to match");
return false;
}
t.pop_back();
s.pop_front();
continue;
}
auto jt = ll1t[t.back()].find(s.front());
if (jt == ll1t[t.back()].end()) {
// std::println("No item to match, failed to match");
return false;
}
auto const &prod = jt->second;
// std::println("Using production {}", prod);
assert(prod.from == t.back());
t.pop_back();
if (!is_epsilon(prod.to))
t.insert_range(t.end(), prod.to | std::views::reverse);
}
// No need to check eol because t won't contain eol. TODO: maybe this should
// be checked?
// std::println("All symbols in t matched, matching ok");
return true;
}
递归下降法
若 \(\text{SELECT}(A \to \beta)\) 间无交集,则可用递归实现 switch (symbol) { case beta[0]: case
beta[1]: }。
一个抽象的 C++ 实现,见 GitHub compilers-labs。
This post is licensed under
CC BY 4.0
by the author.