目录
推荐视频:https://www.bilibili.com/video/av17404276/?p=87 中科大华保健老师
概要
编译器(compiler)和解释器(interpreter)
编译器将源程序翻译成一个等价的、用另一种语言(目标语言)编写的程序。
解释器并不通过翻译的方式生成目标程序,其特点是逐个语句地执行源程序。
对比:由编译器产生的机器语言目标程序通常比一个解释器快很多,而解释器的错误诊断效果通常比编译器更好,因为它逐个语句地执行源程序。
1.词法分析
1.1正则表达式转NFA
Thomson算法过程,注意连接时并不是加上一条无用的ε边,先将这个字符的Thomson的形式独立地画出来,然后用另一堆的最后一个状态(暂时的终结状态)替换掉start指向的那个状态。记得加初始状态箭头。
记住基础的形式
1.2 NFA转DFA
子集构造算法/工作表算法
q0 <- eps_clouse(n0)
Q <- {q0}
worklist <- q0
while(worklist!=[ ])
{
remove q from worklist
foreach(character c) //256 for ASCII
{
t <- eps_clouse(delta(q,c)) //q集合中能接收该字符的状态,对其进行状态转换后再求
eps闭包所得到的集合,q包含多个状态时,t是这多个状态接受该字符的后的eps闭包的并集
D[q,c] <- t
if(t\not\in Q)
add t to Q and worklist
}
}
注意接受一个字符得到的新的状态是指原来的A状态集合所有的move(q,a)得到的状态的并集!
例题:
Dtran转换表(状态转换表)(初始的NFA状态集合是第1个状态求闭包)
NFA STATE |
DFA STATE |
a |
b |
{1,2,3,5,7,8} |
A |
B |
C |
{2,3,4,5,7,8,9} |
B |
B |
D |
{2,3,5,6,7,8} |
C |
B |
C |
{2,3,5,6,7,8,10} |
D |
B |
E(终结) |
{2,3,5,6,7,8,11} |
E |
B |
C |
画出的DFA
1.3 DFA最小化
Hopcroft最小化算法:合并状态,基于等价类
split(S)
foreach(character c)
if(c can split S)
split S into T1,...Tk
hopcroft()
{
split all nodes into N,A //非接受/接受状态
while(set is still changes)
split(S)
}
先将终结/非终结状态划分成A和N集合,再看哪个终结符能划分集合(集合中的状态接受该终结符能跳转到不同的集合则划分)
step1:N A {q0,q1,q2,q4} {q3,q5}
step2:e字符将N split -> {q0,q1} {q2,q4} {q3,q5}
step3:e字符将{q0,q1} split -> {q0} {q1} {q2,q4} {q3,q5}
2.语法分析
2.1 自顶向下分析
消除左递归 & 提取左公因子
LL(1)分析
整理流程:NULLABLE集(能推出ε的非终结符的集合)->FIRST(N)集(从非终结符N推导的句子开头的所有可能的终结符的集合)->FOLLOW(N)集(紧跟在非终结符N后面的终结符号的集合)->FIRST_S(p)集(每个产生式推导的句子开头的所有可能的终结符的集合)
注意:该方法与龙书上的不同
PS:ε在计算FIRST_S时视为属于NULLABLE的非终结符(如果一个产生式右部只有ε,那么使用其FOLLOW集),且计算FIRST集时不视为终结符
如何使用LL(1)分析表?
栈中为非终结符,匹配前看符号(要检验的句子)的字符从而决定将哪一条产生式的右部压入栈,然后匹配成功(一般情况下)又弹出该字符。
step1:NULLABLE集:对所有的产生式循环,NULLABLE集合初始化为空集,记录右边第一个字符可能为ε的非终结符,如果右边为1个ε或ε开头,则该非终结符加入NULLABLE;如果右边只有非终结符且它们全部属于NULLABLE,则该非终结符加入NULLABLE。(这个直接写基本上能看出来)
step2:带NULLABLE的FIRST集构造
如果产生式右边是终结符a则FIRST(N)=a,如果是非终结符就并上其FIRST集,再看其是否属于NULLABLE,属于则继续往下,否则结束。
step3:FOLLOW集
为什么temp一开始要等于FOLLOW(N)?关于这一点,比如产生式X->Y
表示X能推出Y,那么temp一开始就等于FOLLOW(X),然后FOLLOW(Y)就∪=temp了,可以这么理解,X能推出Y那么跟着X的非终结符不就也可能跟着Y,所以才这么初始化。
step4:产生式的FIRST集(FIRST_S)
理解:FOLLOW集的用途,如果该产生式右部的非终结符全部属于NULLABLE集合(即都有可能为ε),那么就必须知道这个非终结符后面可能跟着哪些终结符,否则无法正确得出什么时候使用该产生式。这就是FOLLOW集合所做的事。
注意下面的ε!计算FIRST_S时视为属于NULLABLE的非终结符(如果一个产生式右部只有ε,那么使用其FOLLOW集),且计算FIRST集时不视为终结符。
2.2自底向上分析
LR(0)
从左至右读入程序(L),最右推导(R),零个前看符号(0)(这个0有特殊含义)
点记号:为了方便标记语法分析器已经读入了多少输入,我们可以引入一个点记号