上下文无关文法
与正规式属于同一层面,表达语法分析的基本单元
越接近根节点的运算优先级越低
运算符左结合和右结合的判断:
S=A+S
则+是右结合
A=A*id
则*是左结合
CSL上下文有关文法,sensitive
CFL上下文无关文法,free
下推自动机PDA
相比NA自动机,多了下推栈和下推栈内字母表
给定语言,如何写出他的下推自动机?
例如L={ancbn,n>=0}
- 写出他对应的文法,S→aSb | c
- 生成相应的PDA M
这4个映射关系的含义
1,2,3的映射代表了栈顶是a,读头指向了a,用空串替换了栈里的a
4的映射代表了读头指向了S,栈中没有与之匹配的,故用空串表示,然后向栈里压入S的文法表达,aSb或者c
PDA的推导过程,首先收到语言aacbb,栈内是S,表头指向a,因此用aSb替换掉栈内的S,此时栈顶变为a,a对a,抵消,以此类推,如果能抵消掉整个字符串,即最后#对#,代表这个语言符合语法,与定义的文法保持一致;
自上而下语法分析
自上而下分析的含义
从符号开始从左到右推导出最后的句子,自上而下建立它的分析树,自上而下分析是一个试探的过程,由PDA的分析过程,自上而下分析方法就是把开始符号,反复用不同的产生式进行匹配,如果栈顶符号总是可以与开始符号一致,则这个句子符合语法
自上而下分析最大的问题就是,计算机难以判断到底要使用哪种方式进行推导,对于计算机来说,需要反复尝试是用S=aSb还是S=c来进行推导,这个过程称为回溯,PDA即带有回溯的自上而下分析方法
并且若有A→ab1,A→ab2这种产生式,会发生虚假匹配和大量回溯,造成分析效率极其低下,因此我们需要提取公共左因子;如果出现A→Aα,则分析会进入死循环,这种情况称为直接左递归
二义性
消除二义性的方法
- 改写二义文法为非二义文法
- 规定符号优先级
改写二义文法的例子,比如对于产生式
E→ E+E
| E*E
| (E)
| -E
| id
可以改写为
E→E + T | T
T→T * F | F
F→(E) | -F | id
引入了新的非终结符,使得每一步的推导只有唯一的结果,消除了二义性
消除左递归
消除直接左递归的方法
按照算法,构造一个右递归,比如对于文法E→Eα | β
构造新文法E→βE’,E’→E’α | ε
一切消除左递归的题目都可以将文法里的语言理解哪一部分是α,哪一部分是β,再进行如上的等效替换
如何消除间接左递归?
消除左递归的算法如下
for i in 2..n
loop for j in 1..i-1
loop
①用Aj→δ1|δ2|...|δk的右部替换每个形如Ai→Ajγ产生式中的Aj,
得到新产生式:Ai→δ1γ|δ2γ|...|δkγ;
②消除Ai产生式中的直接左递归;
end loop;
end loop;
该算法的思想在于,首先对非终结符重新排序,对产生序号小于自己的非终结符,即使有左递归,也不做处理,对产生序号大于自己的非终结符,按代入,然后消除直接左递归的方法处理,由此得到一个不含左递归的文法;注意:应用消除左递归算法必须对文法化简,P→P和P→ε是不可以出现的
提取公共左因子
即使消除了左递归,程序编译时依然会不停地回溯来匹配,这样是非常浪费效率的,因此我们需要消除回溯,消除回溯的方法即预测分析程序或者提取公共左因子
求候选式的终结首符集First(α)
在递归下降的语法分析中,文法中的每个非终结符对应一个子程序,每个子程序的过程体按产生式的候选项分情况展开,遇到终结符时匹配终结符,遇到非终结符时调用子程序展开非终结符
预测分析程序
写出first(α)即终结首符集合和follow(S)随符集,first集合内的元素为所有文法里经过推导得出的每个文法的第一个终结符,ε如果被产生,也将包括在first集合里,求first集合即是对全部非终结符产生式求出它们的第一个非终结符开头的符号,如果E=Ta,则FIRST(E)=FIRST(T)
随符集的意义:为了防止F=ab… | ε和E=c在栈中时,编译器不知道如何匹配c,其实应该F=ε而E=c进行匹配,然而编译器并不知道,因此需要创建随符集,follow(F)=c
- 首先加入#到FOLLOW(S)之中,S是开始符号,#是输入结束标记
- 若有产生式A→αBβ,除ε以外,FIRST(β)全部加入到FOLLOW(B)之中
- 若有产生式A→αB或A→αBβ,并且ε∈FIRST(β),则FOLLOW(A)的全体加入FOLLOW(B)
根据上面三条基本规则,将所有非终结符的FOLLOW集合都排完
最后,填写预测分析表,表中非终结符的展开项填入自己的FIRST集合里,展开项包含ε则填入FOLLOW集合里
自下而上语法分析
自下而上语法的核心逻辑是归约,通过把接收到的字符串不停压入栈中,形成句柄的时候进行归约,最终得到开始符号S
自下而上语法分析的难点,如何找到句柄?因为找不到句柄计算机不知道什么时候归约
句柄的定义
最左边父子关系树中所有从左到右排列的叶子(句柄是唯一的)
规范归约(最左归约)
每一步都要剪去句柄,从而暴露出下一个句柄,每次归约完成以后,句柄都会发生变化
四种不同的分析表构造方法
LR(0)
SLR
LR(1)
LALR
LR(0)
构造LR(0)分析表的基本策略:
构造文法G的一个有限自动机,他能识别文法中的所有活前缀
活前缀
规范句型的一个前缀,这种前缀不包含句柄之后的任何符号
引入活前缀的目的是为了找到句柄,进行归约
可以看到abbcde的活前缀是ε,a,ab;活前缀可以简单地定义为在句柄之前的所有前缀,按照最左归约,abbcde的第一个句柄应该是b,而活前缀可以是ε,a,ab
归态活前缀:活前缀的尾部正好是句柄尾部
非归态活前缀:活前缀还需要继续推进
项目
引入项目是为了判断元素是否进栈,方便构造DFA
构造识别活前缀DFA的过程
- 计算DFA的初态,将其加入到闭包之中
- 计算初态之下每个可能的状态转移,计算经X的下一状态
- 反复重复计算,直到没有新状态加入(表达式内所有的值都已经进栈)
如何从DFA到分析表?
SLR(1)
SLR是LR(0)的改进,用于解决冲突
如何判断一个文法是不是SLR(1)?
如果这个文法识别活前缀的DFA存在冲突,则它肯定不是LR(0)
如果这个冲突可以解决,那他就是SLR(1)
来源:CSDN
作者:Aeroblaze
链接:https://blog.csdn.net/qq_43492699/article/details/103762348