编译原理基础:FIRST 集合与提取公共左因子
嗨!如果你正在学编译原理,可能会觉得有些概念听起来很高大上,比如 FIRST 集合和提取公共左因子。别担心,今天我们就来聊聊这两个东西,用简单的话讲明白它们是什么,以及为什么它们在编译器设计里很重要。
FIRST 集合:从哪开始?
感性理解
想象你在写一个程序,编译器得搞清楚你的代码从哪里开始解析。比如你写了个表达式,编译器得知道第一个符号可能是啥------是数字?变量?还是括号?这个"第一个可能出现的符号"的集合,就是 FIRST 集合。简单来说,它就像是编译器的小抄,告诉它:"嘿,这串代码开头可能是这些东西哦!"
形式化表达
在文法里(比如上下文无关文法),FIRST 集合是针对某个非终结符(non-terminal)或产生式,计算出它能推导出的字符串的第一个终结符(terminal)的集合。
定义一下:
- 对于文法中的非终结符 A,FIRST(A) 就是所有从 A 推导出的字符串的第一个终结符的集合。
- 如果 A 能推导出空串 ε,那么 ε 也算在 FIRST(A) 里。
举个例子:
假设有文法:
r
E → T + E | T
T → id | ( E )
- FIRST(T) 是啥?T 可以推导出 "id" 或 "( E )",所以 FIRST(T) = { id, ( }。
- FIRST(E) 呢?E 可以推导出 T 开头的东西,所以 FIRST(E) = FIRST(T) = { id, ( }。
这个集合的作用是啥?它帮编译器在预测式分析(比如 LL(1) 文法)里快速判断下一步该走哪条路。
提取公共左因子:让文法更"干净"
感性理解
假设你在写代码,编译器看到你写的语句开头老是重复,比如"if (条件) ..." 和 "if (条件) ...",它会觉得有点晕:这两条路开头一样,咋分啊?提取公共左因子就像是把这些重复的开头抽出来,整理一下,让编译器更容易看懂你的文法。
形式化表达
在文法里,如果一个非终结符的多个产生式有相同的开头(公共左因子),就会导致解析时出现二义性。提取公共左因子的方法是把这些公共部分抽出来,变成一个新的产生式,剩下的部分单独处理。
比如:
css
A → α β1 | α β2
这里 α 是公共左因子,可以改成:
css
A → α A'
A' → β1 | β2
举个例子:
假设有文法:
bash
S → id = E | id + T
两个产生式都以 "id" 开头,提取公共左因子后变成:
bash
S → id S'
S' → = E | + T
这样,编译器看到 "id" 就知道先走 S,然后再看后面是 "=" 还是 "+",一步步解析就清晰多了。
为什么这两个东西重要?
- FIRST 集合:它是预测式语法分析的核心工具,帮助编译器快速决定从哪个产生式开始推导,避免瞎猜。
- 提取公共左因子:它能消除文法中的二义性,让编译器解析时不"撞车",特别在 LL(1) 文法里很关键。
小结
FIRST 集合像是文法的"导航仪",告诉编译器每一步可能的方向;提取公共左因子像是"整理大师",把乱糟糟的文法收拾得井井有条。学编译原理的时候,这俩是基础中的基础,理解了它们,你就离写一个自己的编译器更近一步啦!下次我们再聊聊 FOLLOW 集合和怎么用这些工具做预测分析,加油哦!