编译原理 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:

S A B a b *
1 1 1 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=0for j in range(3,len(i)):sum=sum+dic[i[j]]if sum==0:dic[i[0]]=0return dic
def first():#求每一个非终结符的FIRST集dic=judge_null()global ff=[]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=0for i1 in range(3,len(j)):if dic[j[i1]]==0:oo=oo+1if 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])breakprint('非终结符的FIRST集依次为:')print(f)
def first_chuan():#求每一个产生式右部符号串的FIRST集global f1dic=judge_null()f1=[]for i in range(len(a)):f1=f1+[set()]for i in a:k=a.index(i)oo=0for j in range(3,len(i)):if dic[i[j]]==0:oo=oo+1if 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 i
def follow_():#计算非终结符的FOLLOW集dic=judge_null()global followfollow=[]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 ss=[]for i in range(len(a)):s=s+[set()]for i in a:k=a.index(i)oo=0for j in range(3,len(i)):if dic[i[j]]==0:oo=oo+1if 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=0for 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=1if zz==1:return 1else:return 0
def pipei():#匹配字符串if judge()==1:return 0c=list(input('请输入待匹配子串'))st=['#',start]while True:i=0for k in range(len(s)):if st[len(st)-1]==a[k][0] and c[0] in s[k]:i=1st.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 0c.remove(c[0])st.pop()if c==['#'] and st==['#']:print('Accept')return 0                  if i==0:print('error')return 0
def run():put_in()first()first_chuan()follow_()select()judge()pipei()
run()

程序运行结果

样例1,

样例2

编译原理 LL1文法的判断和句子识别相关推荐

  1. java实验文法报告_西安邮电大学编译原理LL文法分析器实验(java).doc

    西安邮电大学编译原理LL文法分析器实验(java) <编译原理>实验报告 题目: 语法分析器的制作 学生姓名: 班 级: 软件1202 学 号: 指导教师: 成 绩: 西安邮电大学计算机学 ...

  2. 编译原理实验 -- 文法分析

    编译原理实验 – 文法分析 终结符 和 非终结符 终结符 通常使用小写字母表示,例如 abcdef 非终结符 通常使用大写字母表示,例如 ABCDEF 产生式 通俗来说,就是由终结符和非终结符组合成的 ...

  3. 编译原理:文法及其二义性(判定及消除)

    (请先看置顶博文)https://blog.csdn.net/GenuineMonster/article/details/104495419 学编译原理时,会学到文法,老师在介绍完文法的相关定义后又 ...

  4. 编译原理——证明文法的二义性(1)

    目录 推导和语法树 推导 语法树 文法二义性 在证明文法的二义性之前,我们需要熟悉几个基本的概念. 推导和语法树 推导 这里的推导,简单的来说就是指根据给出的句型(句子),对文法进行推理变化最终得到句 ...

  5. 编译原理(文法、符号表)

    文法 在计算机科学中,文法是编译原理的基础,是描述一门程序设计语言和实现其编译器的方法(文法是用于描述语言的语法结构的形式规则). 1个文法 G 是一个四元组 G = (VN , VT , S, P) ...

  6. 编译原理-正则文法与正则表达式的相互转化

    正则文法与正则表达式的相互转化 前言 一.正则文法 1.定义 2.例子 二.正则表达式 1.定义 2.例子 三.转换规则 1.正则文法转换为正则表达式 2.正则表达式转换为正则文法 四.转换例子 1. ...

  7. 编译原理:文法和语言总结与梳理

    1. 梳理第二章的内容,写一篇理解与总结. 一. 对程序设计语言的描述从语法.语义和语用三个因素考虑: 语法:对语言结构的定义: 语义:语言的含义: 语用:从使用的角度描述语言. 形式语言理论是编译的 ...

  8. 编译原理——证明文法具有二义性

    证明一个文法具有二义性我们需要掌握两个知识点. 1.语法分析树 定义很简单,就是把一个句型的推导写成树的形式,这种表示法就叫语法分析树,或者简称为语法树.大概是这个样子的 2.二义性 一个文法存在某个 ...

  9. 编译原理chatpter04 文法的形式和文法的类型

    一文法的形式一般有很多种,只要能正确的描述出终结符非终结符约定.以及开始符号就行 第一种 ②第二种: 二.句型和句子 只要是在整个推到过程的所有符号都是句型: 句子是只含有终结符. 三.四种文法 0型 ...

最新文章

  1. 模型可视化_20210208
  2. php 当前页面下载文件,php实现当前页面点击下载文件的简单方法
  3. 牛逼,国产开源的远程桌面火了,只有9MB,支持自建中继器!
  4. 引用外部jQuery地址
  5. 你需要知道的MySQL开源存储引擎TokuDB
  6. LambdaMART的源码分析:一(MART:回归树)
  7. STM32下载程序时的一个奇怪现象
  8. 用到的oracle sql语句-001
  9. C++有用的字符函数库
  10. c++从入门到精通_资料下载:从入门到精通,手把手教你学DSP
  11. mysql数据没有同步更新_MySQL数据库主从没有同步的两种解决方案
  12. Git (10)-- 打标签(git tag)
  13. [Data]Link cut tree
  14. android软件安装到平板,新人看过来 安卓平板装机必备软件推荐
  15. 计算机应用基础练习题库与答案
  16. hitb2018_gundam —— tcache double free
  17. maya中英转换插件1.3 更新发布及教程
  18. 【GISer必备】150+地图数据免费下载
  19. 小红书怎么看关键词排名?如何提升笔记自然搜索排名
  20. Photoshop制作WiFi图标

热门文章

  1. 数据库作业——汽车租赁系统
  2. 新拉下来的vue项目怎么跑起来?
  3. Moodle LMS 开源教育系统
  4. 干货,看微信小程序后台用户数据如何演变和递增
  5. Jmeter性能测试-插件
  6. 微信支付api unauthorized【已解决】
  7. VS中*.clw *.ncb *.opt *.aps这些文件是做什么用的?
  8. 三阶齐次线性方程求通解_非齐次线性方程通解求法------常数变易法.ppt
  9. 【报告分享】轻食餐饮发展指南——从入门到可持续经营-美团(附下载)
  10. matlab [c, lags]=xcorr(y1,y2) 转python