版权声明:本文为原创文章,未经博主允许不得用于商业用途。
语法分析
自底向上的语法分析
自底向上即从输入字符串w产生文法开始符号S的过程,相当于从叶子节点反向构造语法分析树。对输入从左到右、自底向上的语法分析可以最终反向构造出一个最右推导。
归约:将与某个产生式匹配的子串替换为产生式头部的非终结符号。
句柄:最右句型中和某个产生式匹配的子串,即若 S ⇒ r m α A w ⇒ r m α β w S\underset{rm}{\Rightarrow}\alpha Aw\underset{rm}{\Rightarrow}\alpha \beta w Srm⇒αAwrm⇒αβw,则 β \beta β就是 w w w的一个句柄。
- 在最右句型中,由于按照从左到右的顺序,句柄右侧只有终结符号
- 无二义性的文法中,句型的句柄唯一。
移入-归约分析技术
移入-归约技术中,使用一个栈保存已经归约好的符号,开始时栈为空(只包含$),通过不断归约栈中符号并移入新的符号完成自底向上的分析。且归约时,句柄总在栈的顶部。
类似自顶向下语法分析,当语法出现二义性时会发生规约冲突,例如之前的if-then-else匹配问题,当输入为if-then时,分析器无法判断后边是否还有else语句,因此不能确定是否应该将if-else归约为stmt。
LR语法分析技术(简单LR技术)
LR(k)中的LR和k分别代表从左向右扫描输入字符、最右推导、每一步只需向前看k个输入字符。由于可以"“向前看”,因此LR技术不需要回溯。"随着“向前看”字符数的增加,状态机的状态数呈指数级增长,而k=1,2时已经可以解决绝大部分问题,因此只考虑这两种情况。最简单的LR技术即 L R ( 0 ) LR(0) LR(0)
- 项:产生式加上在其中某处的一个点。(如 A → X ⋅ Y X A\rightarrow X\cdot YX A→X⋅YX,可以理解为当前自动机已经完成了 点 前边所有符号 X X X的移入/归约,正准备解析 点 后面的符号 Y X YX YX,并且最后会规约为 A A A)
- 增广文法:G的增光文法G’即在G中加入新的开始符号S’,和产生式 S ′ → S S'\rightarrow S S′→S,而按照此产生式归约的过程即表示已经将输入串的所有符号规约到开始符号。
- 项集闭包(closure):项集闭包可以看作需要自底向上构建到当前项集所需的最小项集合。
- 对于
C
L
O
S
U
R
E
(
I
)
CLOSURE(I)
CLOSURE(I),初始化时I在集合中,通过不断迭代下式计算出项集闭包:
- 如果 A → α ⋅ B β A\rightarrow \alpha\cdot B\beta A→α⋅Bβ在闭包中,且 B → ⋅ γ B\rightarrow \cdot \gamma B→⋅γ,则将 B → γ B\rightarrow \gamma B→γ加入到闭包中
- 对于
C
L
O
S
U
R
E
(
I
)
CLOSURE(I)
CLOSURE(I),初始化时I在集合中,通过不断迭代下式计算出项集闭包:
- GOTO函数: G O T O ( I , X ) GOTO(I,X) GOTO(I,X)表示通过I项集,接收到X后可以到达的状态集合。(例如 [ A → α ⋅ X β ] , A ∈ I [A\rightarrow \alpha\cdot X\beta],A\in I [A→α⋅Xβ],A∈I,则 C L O S U R E ( [ A → α X ⋅ β ] ) CLOSURE([A\rightarrow \alpha X\cdot\beta]) CLOSURE([A→αX⋅β])在集合中)
- 项集族:项集族C表示一个增广文法G‘所有的状态集合,可一通过迭代下式计算:
- 初始时, C = { C L O S U R E ( { [ S ′ → S ] } ) } C=\{CLOSURE(\{[S'\rightarrow S]\})\} C={CLOSURE({[S′→S]})}
- 对于C中的项集I,任一文法符号X,如果 G O T O ( I , X ) GOTO(I,X) GOTO(I,X)不在C中,则加入其中。
在LR(0)自动机中,每个状态对应一个项集,如果其中:
- 存在形如 A → α ⋅ A\rightarrow \alpha\cdot A→α⋅的项,则表示可能找到了一个最右句型的句柄,接下来可以规约为A。
- 存在形如 A → α ⋅ X A\rightarrow \alpha\cdot X A→α⋅X的项,表示目前已接收的输入如果接下来接收(移入)X则可能会规约为A。
在实现时通过一个栈记录已经移入/归约的状态序列并不断查找分析表完成分析。
LR分析表
结构
LR分析表每行代表一个状态,共包括两个部分:ACTION和GOTO。
- ACTION:ACTION部分的列表头代表终结符,每个单元包含如下四种操作之一:
- 移入j:将j状态移入栈中
- 归约 A → β A\rightarrow \beta A→β:按照此产生式将栈顶项归约为A
- 接受:完成整个输入串的分析,并接受此输入
- 报错:出现语法错误,停止分析
- GOTO:GOTO部分的表头为非终结符A,描述当前状态接收到新归约的A符号后的下一个状态。则如果GOTO函数 G O T O ( I i , A ) = I j GOTO(I_i,A)=I_j GOTO(Ii,A)=Ij,则 G O T O [ i , A ] GOTO[i,A] GOTO[i,A]表项为j。
使用方法
使用LR分析表步骤如下:
- 格局表示当前栈中内容和余下的输入内容,记作 ( s 0 s 1 . . . s m , a i a i + 1 . . . a n (s_0s_1...s_m,a_ia_{i+1}...a_n (s0s1...sm,aiai+1...an$ ) ) ),可以看作时当前的上下文。分析的过程实际是格局不断变化的过程。
- 在当前格局,LR状态机查找分析表
A
C
T
I
O
N
[
s
m
,
a
i
]
ACTION[s_m,a_i]
ACTION[sm,ai]获取下一步的动作:
- 移入s:将 a i a_i ai对应的状态s移入栈中,并分析下一个符号,格局变为 ( s 0 . . . s m s , a i + 1 . . . a n (s_0...s_ms,a_{i+1}...a_n (s0...sms,ai+1...an$ ) ) )
- 归约 A → β A\rightarrow \beta A→β:将栈顶的 ∣ β ∣ |\beta| ∣β∣个状态规约为A,进入新状态 G O T O [ s m − r , A ] GOTO[s_{m-r},A] GOTO[sm−r,A],格局变为 ( s 0 . . . s m − r s , a i . . . a n (s_0...s_{m-r}s,a_i...a_n (s0...sm−rs,ai...an$ ) ) )
- 接受
- 报错
- LR分析器从初始栈 s 0 s_0 s0开始,不断分析输入,知道收到 接受/报错 指令结束。
例
$\small{其中,s_i:移入状态i,r_j:按照j产生式归约} $
当输入串为 i d ∗ i d + i d id*id+id id∗id+id时,栈的改变如下:
- 0
- 初始栈中状态为0,下一个符号为 i d id id, A C T I O N [ 0 , i d ] = s 5 ACTION[0,id]=s5 ACTION[0,id]=s5,移入状态5,格局中输入变为 ∗ i d + i d *id+id ∗id+id$,接受符号为 i d id id
- 0,5
- A C T I O N [ 5 , ∗ ] = r 6 ACTION[5,*]=r6 ACTION[5,∗]=r6,因此按照产生式6( F → i d F\rightarrow id F→id)归约,接受符号变为 F F F,回退并压入 G O T O [ 0 , F ] = 3 GOTO[0,F]=3 GOTO[0,F]=3,格局中输入不变
- 0,3
- A C T I O N [ 3 , ∗ ] = r 4 ACTION[3,*]=r4 ACTION[3,∗]=r4,按照产生式4( T → F T\rightarrow F T→F)归约,接受符号变为 T T T,回退并压入 G O T O [ 0 , T ] = 2 GOTO[0,T]=2 GOTO[0,T]=2
- 0,2
- A C T I O N [ 2 , ∗ ] = s 7 ACTION[2,*]=s7 ACTION[2,∗]=s7,移入状态7,格局中输入变为 i d + i d id+id id+id$,接受符号为 T ∗ T* T∗
- 0,2,7
- A C T I O N [ 7 , i d ] = s 5 ACTION[7,id]=s5 ACTION[7,id]=s5,移入状态5,格局中输入变为 + i d +id +id$,接受符号为 T ∗ i d T*id T∗id
⋮ \vdots ⋮
- 0,1
- A C T I O N [ 1 , ACTION[1, ACTION[1,$ ] ] ]=acc$,接受输入,分析完成
构造SLR语法分析表
构造方法如下:
- 首先构造G’的LR(0)项集规范族 { I 0 , I 1 , . . . I n } \{I_0,I_1,...I_n\} {I0,I1,...In}
- 构造状态i项集
I
i
I_i
Ii对应的ACTION部分:
- [ A → α ⋅ a β ] ∈ I i [A\rightarrow\alpha\cdot a\beta]\in I_i [A→α⋅aβ]∈Ii,且 G O T O ( I i , a ) = I j GOTO(I_i,a)=I_j GOTO(Ii,a)=Ij,则应该执行移入操作, A C T I O N [ i , a ] = 移 入 j ACTION[i,a]=移入j ACTION[i,a]=移入j
- [ A → α ⋅ ] ∈ I i [A\rightarrow\alpha\cdot]\in I_i [A→α⋅]∈Ii,应该执行归约,则对于 F O L L O W ( A ) FOLLOW(A) FOLLOW(A)中的所有a, A C T I O N [ i , a ] = 按 照 A → α 归 约 ACTION[i,a]=按照A\rightarrow\alpha归约 ACTION[i,a]=按照A→α归约
- [ S ′ → S ⋅ ] ∈ I i [S'\rightarrow S\cdot]\in I_i [S′→S⋅]∈Ii,则 A C T I O N [ i , ACTION[i, ACTION[i,$] = a c c =acc =acc
- 构造GOTO部分。
- 将表中剩余部分都填入 e r r o r error error
如果在构造SLR表过程中没有冲突,则该文法为SLR的。
更强大的LR语法分析器(LR(1))
考虑如下情况:项 [ A → α ⋅ ] [A\rightarrow \alpha\cdot] [A→α⋅]出现在 I i I_i Ii中的条件为 B → β ⋅ A γ B\rightarrow \beta\cdot A\gamma B→β⋅Aγ在项集中。
因此假设 F O L L O W ( A ) = { a } ∪ F I R S T ( γ ) FOLLOW(A)=\{a\}\cup FIRST(\gamma) FOLLOW(A)={a}∪FIRST(γ),则按照当前语法,当A下一个符号为 F I R S T ( γ ) FIRST(\gamma) FIRST(γ)时,可以归约;当下一个符号为a时,不应该进行归约。
而LR(0)无法完成正确归约。
因此需要往前预看一位符号的更强大分析器LR(1)解决此类规约问题。在LR(1)中可以根据下一个待分析符号准确判断是否应该归约。
- 项:LR(1)中的项包含下一个符号,即形如 [ A → α ⋅ β , a ] [A\rightarrow \alpha\cdot \beta,a] [A→α⋅β,a],a为下一个符号,可以为终结符或结束符$
- 归约:归约时,下一项必须为a时才可以归约
- 移入:当 β \beta β不为空时,不许考虑a,直接将a传递给下一个状态。
- CLOSURE:当由 [ A → α ⋅ B β , a ] [A\rightarrow \alpha\cdot B\beta,a] [A→α⋅Bβ,a]加入 [ B ⋅ θ , b ] [B\cdot \theta,b] [B⋅θ,b]时,b需要为下一个可接受字符,即 F I R S T ( β α ) FIRST(\beta\alpha) FIRST(βα)
LR(1)为每一个向前看符号构造了新的状态,可以看作比LR(0)多了一个自由度,因此状态数多许多。