这是我参与「第五届青训营」伴学笔记创作活动的第15天。本篇为第五届字节跳动青训营-寒假专场-后端基础课程的笔记
构建规则引擎的重要一步就是对输入进行语法分析,今天分享常见的语法分析方法。
常见的语法分析方法
- LL
- LR(1)
- SLR(1)
- LR(1)
- LALR (1):LookAhead(1)LR(0)
- 在LR 1 的基础上,我们合并一组ITEM中 item相同,但是后续符号不同的为一个
- 如果自动机还能正常工作,那么这将是对语法产生更多的限制:从LR(1)到LALR(1)将会引入reduce-reduce 冲突
- 从LR(1)构造 LALR (1)自动机?可行,但是还有更加直接的方式(更加高效的)
关系图
几种文法的关系图如下:
自底向上分析法
LR 0
为啥这个名字
Left to Right scan
Rightmost derivation
zero Token look ahead
构建LR 自动机(直接从文法构造DFA)
步骤:
-
构建增广文法
-
将S'-> *S 作为初始状态,构建它的闭包以生成完整的初始状态。
-
构建GOTO ——状态集合之间的转换,捕获一个(非终结符或者终结符)
-
重复直到没有新的集合产生
LR0 的缺点
reduce-reduce conflict
如果一个状态中,包含二个及以上的LR 0项可规约,这时候基本的LR并无法区分进行哪种规约。
shift-reduce conflict
如果一个状态中,包含可规约和可以shift,这也无法判断。
不是LR 0 的文法
如果一个文法对应自动机内有shift-reduce or reduce-reduce conflict,就不是LR(0)文法。
可行前缀:viable prefix
sequence of symbols on the parsing stack
在解析栈中的一串符号
如果不能在继续通过 shift操作入栈(延长这个symbol串的长度),即进行reduce,stack 和 input 构成的右句型就不是可行前缀。
只要能在栈中,就是右句型的活前缀。
LR (0) 弱爆了
没有利用后文(右侧)的信息,这样是低效的,容易冲突。
LR(0) 的表
y坐标轴:状态集合(items in I)
x坐标轴:终结符 concatenate 非终结符 concatenate 状态遇到前两者的状态
转移方式:
shift (同时需要在 状态和符号交点处表明下一跳转)
reduce using which production
accept
空的Entry:如果进入此状态,那么匹配失败发现 error
SLR(1)
这是对于LR(0)的升级。现在可以Peek next Token,并且在可能发生规约的时候,根据获得的非终结符的FOLLOWSET来确定是否应该/发生哪一个Reduce操作.
SLR(1) 使用LR(0) 的parse table
优点
利用小空间O(1) ,解决了许多冲突。
相比LR(0),其适合的语法更多。
SLR(1) 的弱点也很明显
我们根据FOLLOWSET来判断用哪个跳转,其实是非常粗糙的,可能FOLLOWSET的某些元素不会出现在右句型里面,此时我们按照SLR(1)的规则,反而有可能解决不了冲突(“本来peek 1可以解决的冲突”)。
所以需要有更加强大的工具。