<编译原理> 提取左公因子算法

本文实现了一个较为简单的算法——文法的左公因子提取。umm其实也称不上是算法。


我们的目标非常明确,就是将文法产生式的左公因式都提出来,需要注意以下几点:

  1. 在一个产生式中需要提取范围最大的公因式,即拥有该左公因式的右部应该尽可能得多

  2. 提取左公因子之后会产生新的产生式,该产生式也可能存在左公因子,需要一并提取,因此是一种递归的思想

  3. 好像也就这两点…

那么我们只需要从广度和深度进行确定即可。首先确定左公因子广度,即最多的含有左公因子的右部个数。很显然,对于最大的广度,只要找第一个符号相等的右部即可。确定最大广度后,我们再求这些首字符相等右部的最大深度(最长的左公因子)即可,那么就一遍一遍地遍历,直到发现其中一个右部和其他右部的第位字符不同时,退出循环。构造新产生式,然后对新产生式进行左公因子提取即可。


C ☺ D E

def extract(gen, left):
    gen[left].sort()
    k = 0
    flag = True
    next_str = left + "'"
    while k < len(gen[left]) - 1:
        kk = k + 1
        # 确定横向最大距离
        while kk < len(gen[left]) and gen[left][kk][0] == gen[left][kk - 1][0]:
            kk += 1
        if kk == k + 1:
            k += 1
            continue
        # 确定纵向最大深度
        common = gen[left][k][0]
        depth = 0
        for depth in range(1, len(gen[left][k])):
            flag = True
            for i in range(k + 1, kk):
                if depth >= len(gen[left][i]) or gen[left][i][depth] != gen[left][i - 1][depth]:
                    flag = False
                    break
            if flag:
                common += gen[left][k][depth]
            else:
                break
        # 当整个gen[left][k]均为左公因式(flag始终为True)时必须将depth+=1,因为depth指的是左公因式的后一位
        depth += (1 if flag else 0)
        # extract common part
        gen[next_str] = ([gen[left][k][depth:]] if depth < len(gen[left][k]) else ['ε'])
        gen[left][k] = common + next_str
        for i in range(k + 1, kk):
            gen[next_str].append((gen[left][k + 1][depth:] if depth < len(gen[left][k + 1]) else ['ε']))
            gen[left].pop(k + 1)
        # 递归调用
        extract(gen, next_str)
        next_str += "'"
        k = k + 1


n = eval(input('请输入文法产生式的个数:'))
print('请输入文法产生式:')
gen = dict()
for i in range(n):
    g = input().replace(' ', '')
    assert g[1:3] == '->'
    gen.setdefault(g[0], [])
    start = 3
    for i in range(3, len(g)):
        if g[i] == '|' or i == len(g) - 1:
            i += (1 if i == len(g) - 1 else 0)
            assert start < i
            gen[g[0]].append(g[start:i])
            start = i + 1
    extract(gen, g[0])

print("\nAfter left factoring:")
for key in gen:
    print(key + ' -> ', end='')
    print(*gen[key], sep=' | ')

Tips

  1. 数据结构与<编译原理> 左递归消除算法的类似,只是列表中无需分终结符开头还是非终结符开头,提取左公因子一视同仁

  2. 由于相同字符开头的右部可能很分散,不太好直接找,因此我们在提取之前可以对一个产生式的右部先按照字符序进行排序

  3. [k,kk)来表示最大广度范围,depth是最大深度,next_str是下一个新产生式左部的名称(就是每次加一个【‘】)

  4. 注意有时其中一个右部本身就是左公因式的特殊情况,此时提取后的新产生式右部需要加上【ε】


Outcome

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值