编译原理 LL1文法的判断和句子识别
来源:互联网 发布:python 数组拆分 编辑:程序博客网 时间:2024/05/21 22:40
编译原理 LL1文法的判断和句子识别
LL1文法概述
点击查看百度百科
对文法G的句子进行确定的自顶向下语法分析的充分必要条件是,G的任意两个具有相同左部的
产生式A—>α|β 满足下列条件:
(1)如果α、β均不能推导出ε,则 FIRST(α) ∩ FIRST(β) = Φ。
(2)α 和 β 至多有一个能推导出 ε。
(3)如果 β *═> ε,则 FIRST(α) ∩ FOLLOW(A) = Φ。
将满足上述条件的文法称为LL(1)文法。
第一个L代表从左向右扫描输入符号串,第二个L代表产生最左推导,1代表在分析过程中执行每一步推导都要向前查看一个输入符号——当前正在处理的输入符号。
LL(1)文法既不是二义性的,也不含左递归,对LL(1)文法的所有句子均可进行确定的自顶向下语法分析。
需要注意的是,并不是所有的语言都可以用LL(1)文法来描述,而且不存在判定某语言是否是LL(1)文法的算法。也就是说,确定的自顶向下分析只能实现一部分上下文无关语言的分析,这就是LL(1)文法所产生的语言。另外,在上述LL(1)文法的条件中,要求:ε ∈ FIRST(α1),ε ∈ FIRST(α2),…ε ∈ FIRST(αn) 中至多有一个成立。
程序介绍
算法参考张素琴等《编译原理》第二版,清华大学出版社。编程语言和环境:python3.5 ,window86-32bite
求能导出空串的非终结符
1、创建一个键为字母表(包括,用表示空串,下同) ,值为1的字典,并将空串的值变为0:
2、扫描每条产生式,若产生式右部所有的字符对应的值为0,就把左部的非终结符对应的值赋0
3、重复2,直到字典不在改变为止
计算非终结符FIRST集F
1、构建每一非终结符的FIRST集,开始为空
2、对于每一文法符号
若x为终结符,则FIRST(x)={x}
若x为非终结符:
若有x->aG,G为字母表上的符号串,a属于FIRST(x)
若x->,则 属于FIRST(x)
若x的产生式右部全都是非终结符Y1~Yn,若Y1~Yn全部能导出空串,则FIRST(x)=FIRST(Y1)U …U FIRST(Yn) U {*}
若Y1~Yi-1都能推出空串,则FIRST(Yi)-{*},I为1~i-1,和FIRST(YI)都在FIRST(x)中
3、重复步骤2,直到FIRST不在增大
计算产生式右部字符串的FIRST集F1
逐个扫描串上的字母,方法同计算非终结符FIRST集F。
计算FOLLOW集follow
1、构建每一个非终结符的follow集,并给开始符的follow集加上{#}
2、若有A->aBc,则把FIRST(c)的非空元素加入follow(B),若有c->*,则把follow(A)加入follow(B)
3、若有A->aB,则把follow(A)加入follow(B)
4、重复步骤2,3,直到follow不再变化
计算SELECT集S
对于A->a
若a不能导出空,则select(A->a)=FIRST(a)
若a能导出空,则select(A->a)=(FIRST(a)-{*}) U follow(A)
判定是否为LL1文法
遍历产生式,若有相同左部的产生式的交集不为空,则不是LL1文法
若是LL1文法,输入句子匹配
1、构造分析栈st={#,start},start为开始符。
2、从st栈顶选取元素a,从要匹配句子S选取最左边元素b,若a=b,则a,b出栈,若不相等,则:
对于a寻找以a开头的产生式a->(右部)的select集,使b属于select,a出栈,右部逆序入st。若找不到,匹配失败结束程序
3,重复步骤2,直到st和S均为#,匹配成功。
源代码
def put_in():#输入文法五元组 global a,vn,vt,start #a=[list('S->AB'),list('S->bC'),list('A->*'),list('A->b'),list('B->*'),list('B->aD'),list('C->AD'),list('C->b'),list('D->aS'),list('D->c')] #vn=['S','A','B','C','D'] #vt=['a','b','c'] a=[list('E->TA'),list('A->+TA'),list('T->FB'),list('A->*'),list('B->-FB'),list('B->*'),list('F->i'),list('F->(E)')] vn=['E','A','B','F','T'] vt=['+','-','(',')','i'] start='E' print('文法产生式为:') for i in a: print (i)def judge_null():#求能推出空串的非终结符 null=['*'] values=[1]*len(vn+vt+null) dic=dict(zip(vn+vt+null,values)) dic['*']=0 for un_relation in range(5): for i in a: sum=0 for j in range(3,len(i)): sum=sum+dic[i[j]] if sum==0: dic[i[0]]=0 return dicdef first():#求每一个非终结符的FIRST集 dic=judge_null() global f f=[] for i in range(len(vn)): f=f+[set()] for ji in range(5): for i in range(len(vn)): for j in a: if vn[i]==j[0]: oo=0 for i1 in range(3,len(j)): if dic[j[i1]]==0: oo=oo+1 if oo==len(j)-3 and j[3]!='*':#若右部全部是能推出空符的非终结符 for i1 in range(3,len(j)): f[i]=f[i]|f[vn.index(j[i1])] f[i]=f[i]|{'*'} else: if j[3] in vt: f[i].add(j[3]) elif j[3]=='*': f[i].add('*') else: for i1 in range(3,len(j)): if dic[j[i1]]==0: if '*' in f[vn.index(j[i1])]: f[i]=(f[vn.index(j[i1])]|f[i])-{'*'} else: f[i]=(f[vn.index(j[i1])]|f[i]) else: f[i]=(f[vn.index(j[i1])]|f[i]) break print('非终结符的FIRST集依次为:') print(f)def first_chuan():#求每一个产生式右部符号串的FIRST集 global f1 dic=judge_null() f1=[] for i in range(len(a)): f1=f1+[set()] for i in a: k=a.index(i) oo=0 for j in range(3,len(i)): if dic[i[j]]==0: oo=oo+1 if oo==len(i)-3 and i[3]!='*':#若全部是能推出空符的非终结符 for j in range(3,len(i)): f1[k]=f1[k]|f[vn.index(i[j])] f1[k]=f1[k]|{'*'} else: if i[3] in vt:#若是以终结符开头 f1[k].add(i[3]) elif i[3]=='*':#若是空符 f1[k].add('*') else: for j in range(3,len(i)): if dic[i[j]]==0: f1[k]=(f[vn.index(i[j])]|f1[k])-{'*'} else: f1[k]=(f[vn.index(i[j])]|f1[k]) break print('符号串的FIRST集依次为:') print(f1)def index1(li,k): #神奇勿动,求在产生式右部某元素在列表第一次出现的位置 for i in range(3,len(li)): if li[i]==k: return idef follow_():#计算非终结符的FOLLOW集 dic=judge_null() global follow follow=[] for i in range(len(vn)): follow=follow+[set()] follow[vn.index(start)]=follow[vn.index(start)]|{'#'} for iii in range(5): for i in range(len(vn)): for j in a: for k in j[3:]: if k==vn[i]: if index1(j,k)==len(j)-1: follow[i]=follow[i]|follow[vn.index(j[0])] else: if j[index1(j,k)+1] in vt: follow[i]=follow[i]|{j[index1(j,k)+1]} else: if dic[j[index1(j,k)+1]]==1: follow[i]=(follow[i]|f[vn.index(j[index1(j,k)+1])])-{'*'} else: follow[i]=(follow[i]|f[vn.index(j[index1(j,k)+1])])-{'*'} follow[i]=follow[i]|follow[vn.index(j[0])] print('follow集依次为:') print(follow)def select(): dic=judge_null() global s s=[] for i in range(len(a)): s=s+[set()] for i in a: k=a.index(i) oo=0 for j in range(3,len(i)): if dic[i[j]]==0: oo=oo+1 if oo==len(i)-3 :#若全部是能推出空符 s[k]=(f1[k]-{'*'})|follow[vn.index(i[0])] else: s[k]=f1[k] print('select集依次为:') print(s)def judge():#判断文法是不是LL1文法 b=set() zz=0 for i in range(len(a)): for k in range(len(a)): if a[i][0]==a[k][0] and i!=k: b=s[i]&s[k] if len(b)!=0: print('不是LL(1)文法,因为:') print(a[i],'和',a[k],'的select集的交集不为空') zz=1 if zz==1: return 1 else: return 0def pipei():#匹配字符串 if judge()==1: return 0 c=list(input('请输入待匹配子串')) st=['#',start] while True: i=0 for k in range(len(s)): if st[len(st)-1]==a[k][0] and c[0] in s[k]: i=1 st.pop() for j in range(3,len(a[k])): st=st+[a[k][len(a[k])-j+2]] if st[len(st)-1]=='*': st.pop() if st[len(st)-1]==c[0]: if c==['#'] and st==['#']: print('Accept') return 0 c.remove(c[0]) st.pop() if c==['#'] and st==['#']: print('Accept') return 0 if i==0: print('error') return 0def run(): put_in() first() first_chuan() follow_() select() judge() pipei()run()
程序运行结果
样例1,
样例2
- 编译原理 LL1文法的判断和句子识别
- 编译原理LL1文法
- 【编译原理】LL1文法语法分析器
- 判断是不是LL1文法
- 编译原理文法类型判断
- 编译原理:文法和语言
- 编译原理--文法和语言
- 编译原理---文法和语言
- 编译原理:文法类型判断C++实现
- 编译原理-表达式的文法
- 编译原理--文法的理解
- 编译原理:句型分析和有关文法实用的说明
- [编译原理]文法的定义与文法产生的分类
- 编译原理--文法分类和推导树
- 编译原理(2):文法和语言
- 编译原理之ll(1)文法判断和左递归问题
- 编译原理(一) Chomsky文法的判断方法及C++代码实现
- 编译原理(一) Chomsky文法的判断方法及C++代码实现
- 算法导论笔记——水桶排序(基数排序)
- 关于rman增量备份
- 常见的八种导致 APP 内存泄漏的问题
- Windows控件消息反射
- UML---4+1视图
- 编译原理 LL1文法的判断和句子识别
- webstrom 获取注册码地址
- 实现一个函数翻转字符串(将一个字符串的内容,逆序存储)
- Android 常用RGB值及中英文名称
- LeakCanary源码分析第二讲-RefWatcher详解
- DrawerLayout最简单的使用,没有更多的内容
- 从Android ListView 看Observer 观察者设计模式setOnClickListener
- Physics.IgnoreCollision 忽略碰撞
- 郝斌的C语言基础 165,166 枚举