一个编译器的实现2——从文法到LL(1)分析表的概念和算法

关于编译原理基础概念可参考http://www.cnblogs.com/bitzhuwei/archive/2012/10/22/SmileWei_Compiler.html 

关于下列代码的基础数据结构参见http://www.cnblogs.com/bitzhuwei/archive/2012/03/09/compiler_basic_data_structure.html

一、      消除直接左递归

设P -> Pα1 | Pα2 | ... | Pαn | β1 | β2 | ... |βm

其中每个α不为ε(ε就是空,什么都没有的意思,类似null),每个β不以P开头。

则非终结符P可改写为

P -> β1P’ | β2P’ | ... | βmP’

P’ -> α1P’ | α2P’ | ... | αnP’ | null

解释:原来的P展开就是βxαi..αiαj..αj...αt..αt的形式,即某个β开头,各种阿尔法跟随的一个串。所以与改写形式所表达的东西是一样的。

二、      消除间接左递归

给定文法G,若G不含回路(P经过若干步推导又得到P)且不含以ε为右部的产生式。

则其消除左递归的算法如下:

  1. 对G的非终结符按任意顺序排列,如A1, A2, A3, ... , An
  2. for (i = 1; i <= n; i++)
        for (j = 1; j <= i - 1; j++)
        {
            把形如Ai -> Ajγ的产生式改写成Ai -> δ1γ | δ2γ | ... | δkγ的形式,其中Aj -> δ1 | δ2 | ... | δk是关于Aj的全部规则
            消除Ai规则中的直接左递归
        }
  3. 简化由上一步得到的文法,即去掉多余的规则

三、      FIRST集

若文法G为二型文法且不含左递归,则G的非终结符的每个候选式α的终结首符集FIRST(α)为FIRST(α) = { a | α经过0或多步推导为a...的形式,其中a∈VT }

解读:FIRST集的含义是:候选式经过推导,最后就是一个终结符的串,推导过程不同,会有多个不同的串(可能是无限个),这些串里的第一个字符组成的集合就是这个候选式的FIRST集。有了这个FIRST集,就可以知道这个候选式是否能匹配接下来要解析的单词流了。

四、      FOLLOW集

设上下文无关文法(二型文法)G,开始符号为S,对于G中的任意非终结符A,其FOLLOW(A) = { a | S 经过0或多步推导会出现 ...Aa...的形式,其中a∈VT或#号 }

解读:FOLLOW集的含义是:G的一切句型中,能够紧跟着非终结符A之后的一切终结符或井号#。#是当出现 ...A 这样的情况,即A为最后一个字符。

五、      构造FOLLOW集的算法

  1. 令#∈FOLLOW(S)
  2. 若文法G中有形如A –> αBβ的规则,且β≠ε,则将FIRST(β)中的一切非终结符加入FOLLOW(B)
  3. 若文法G中有形如A -> αB或A -> αBβ的规则,且ε∈FIRST(β),则将FOLLOW(A)中的全部元素加入FOLLOW(B)
  4. 反复使用前两条规则,直到所有的FOLLOW集都没有改变。

六、      构造LL(1)分析表的算法

输入:文法G

输出:G的LL(1)分析表M(Ax, ay),其中A为非终结符,a为终结符

算法:

  1. 求出G的FIRST集和FOLLOW集
  2. for (G的每个产生式 A -> γ1 | γ2 | ... | γm)
    {
        if (a ∈ FIRST(γi)) 置 M(A, a) 为 “A -> γi”
        if (ε ∈ FIRST(γi))
            for (每个 a ∈ FOLLOW(A))
                置 M(A, a)为 “A -> γi”(实际上此处的γi都是ε)
    }
    置所有无定义的 M(A, a)为出错。
原文地址:https://www.cnblogs.com/bitzhuwei/p/Compiler_Grammer2LL1Parser.html