编译原理 LL(1)文法判别 python实现

一.上下文无关文法的输入与存储

按文法的四元组输入,用list存储,其中每个产生式用tuple保存,然后存在list中,集合可以通过映射关系存储在dict中。
list:可以存储任意类型,同时可以通过下表索引,用来存储终结符和非终结符,以及存储放在元组里的产生式
set:非常方便的实现判重,同时可以实现很多集合运算,轻松实现first、follow和select的存储运算
tuple:数据不能修改,可以通过下表索引,用来存储产生式,同时可以作为dict的键来索引(list不能作为dict的键)
dict:可以存储字符与 first、follow 集合的映射,产生式和select集合的映射。

二.求出能推出空的非终结符

1.字典 x 标记所有非终结符的初始状态为未定,用 0 表示(能推出空 & 则为 1 ,不能则为 2 )。

2.删除右部有终结符的产生式。若某非终结符对应的产生式都被删除,则将该非终结符标记为 2 ,即不能推出空 & 。

3.若有产生式右部为空 & ,则将左部非终结符标记为 1 ,然后删除该非终结符为左部的所有产生式。

4.扫描每个产生式右部符号,若为能推出空的非终结符则删除,若该符号删除后右部为空,则将左部非终结符标记为 1,并删除该非终结符为左部的所有产生式。

5.扫描每个产生式右部符号,若有不能推出空的非终结符,则删去该产生式,若则使该左部非终结符的所有产生式全被删除,则将该非终结符标记为 2 。

6.重复 4、5 直到字典 x 不再变化,最后得到的字典 x 即记录了非终结符能否推出空。

tips:重复计算直到不再变化,可能好理解,但不好实现,可以设置一个布尔型标志标量,作为循环条件,初始为True,每次进入循环就置为False,如果目标集合发生改变,就置为True。如果用集合做运算可能不好知道目标集合是否改变,可以用一个临时变量提前存储目标集合,计算后再比较是否变化,这里要注意python赋值的浅拷贝问题,记得要深拷贝,可以看我前一篇博文。
tips:更新。突然想到更新目标集合的都是并集运算,集合元素只增不减,可以用临时变量保存计算前长度就行,后面直接比较长度就知道目标集合是否变化。深拷贝再比较肯定会更耗时。

三.计算 first 集合

1.若 A 为终结符,则 first(A) = {A}

2.若 A 为非终结符且能推出空,则将空 & 加入 first(A)

3.遍历产生式,若右部第一个符号为终结符,形如 A->a…,A为非终结符,a为终结符,则将 a 加入 first(A)

4.对每个产生式,从右部第 Xi个符号(i 从 1 开始)开始遍历,若 Xi 能推出空,则将 first(Xi) - {&} 加入 first(左部非终结符),若Xi不能推出空 则将first(Xi) 加入 first(左部非终结符),然后遍历下一个产生式右部,i又从1开始。

5.重复 4 直到 每个符号的 first 集合都不再变化。

6.根据 5 得到每个符号的 first 集合,求每个产生式右部字符串的 first 集合,原理类似 4 ,若右部第一个字符能推出空,就将 first()-{&} 加入字符串的 first() ,然后看第二个…若第n个字符不能推出空,则将 first() 加入字符串的 first() ,然后不再往下看。若一直到倒数第二个字符都能推出空,则处理最后一个字符时,直接将 first() 加入 字符串的 first()中。

tips:写代码时可以循环处理,先将当前字符 first()-{&} 加入正在求解的first() 中,然后再看 & 在不在当前字符 first() 中,不在就break跳出循环。
tips:这里所说的加入操作都是指并集操作 ,将B加入A 即 A = A U B
tips:刚开始写博客,很多数学符号不会敲,见怪不怪啦,看不懂的地方烦请评论区留言或者私我。

四.计算 follow 集合

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值