【编译原理系列】语法分析与上下文无关文法
语法分析
词法分析:
- 字母是元素,组成字符串,记号的集合,线性结构,以字符流为输入
语法分析:
- 记号是元素,组成句子, 句子的集合,树结构,以记号流为输入
语法的双重含意:
- 语法规则:上下文无关文法(子集-LL文法或LR文法)
- 语法分析:下推自动机(LL或LR分析器),自上而下和自下而上分析 (这两种都只能处理上下文无关文法的子类)
语法分析器
语法分析器是编译器前端的重要组成部分,中心部件
语法分析器的两个重要作用:
- 根据词法分析器提供的记号流,为语法正确的输入构造分析树(或语法树)
- 检查输入中的语法(可能包括词法)错误,并调用出错处理器进行适当处理
语法错误处理原则
源程序中可能出现的错误:
- 语法(包括词法)错误
- 词法错误如非法字符或拼写错关键字、标识符等
- 语法错误是指语法结构出错,如少分号、begin/end不配对等
- 语义错误
- 静态语义错误(涉及的是编译时可检查出来的错误):如类型不一致、参数不匹配等
- 动态语义错误(程序运行时的逻辑错误):如死循环、变量为零时作除数等
目标:
- 清楚而准确地报告错误的出现(地点正确,不漏报、不错报也不多报
- 迅速从每个错误中恢复过来(以便分析继续进行)
- 不应对语法正确源程序的分析速度降低太多
基本恢复策略
- 紧急方式恢复:抛弃若干输入,直到遇到某个指定的合法记号(称为同步记号)集合为止同步记号一般是定界符,如分号或end等【最简单,但最容易造成错报、漏报和多报语法错误的现象】
- 短语级恢复:采用串替换的方式对剩余输入进行局部纠正(抛弃+插入)
- 出错产生式:用出错产生式捕捉错误(预测错误),预置型的短语级恢复方式(YACC采用的方式)
- 全局纠正:对错误输入序列x,找相近序列y,使得x变换成y所需的修改、插入、删除次数最少【代价太大】
上下文无关文法CFG
CFG:Context Free Grammar
CFG是一个四元组G =(N,T,P,S),其中
- (1) N是非终结符(Nonterminals)的有限集合;
- (2) T是终结符(Terminals)的有限集合,且N∩T=Φ;
- (3) P是产生式(Productions)的有限集合,
A→α,其中A∈N(左部),α∈(N∪T)*(右部),
若α=ε,则称A→ε为空产生式(也可以记为A →); - (4) S是非终结符,称为文法的开始符号(Start symbol)
可以将产生式中的记号→读作 “定义为” 或者 “可导出”
- 如:“E → E + E”可用自然语言表述为“算术表达式定义为两个算术表达式相加”, 或者“一个算术表达式加上另一个算术表达式,仍然是一个算术表达式”
各元素要求
- 文法开始符号S是第一个产生式的左部;
- N是可以出现在产生式左边符号的集合;
- T是绝不出现在产生式左边符号的集合(记号) ,所以T不一定是一个句子的那种终结符,也可以是一个短语的终结符,如+、-、(、)等等
约定:
- 大写英文字母A、B、C表示非终结符;
- 小写英文字母a、b、c表示终结符;
- 小写希腊字母α、β、δ表示任意文法符号序列
- 产生式中,用“|”连接的每个右部称为一个候选项,具有平等的权利
CFG的产生式表示也被称为巴克斯范式BNF,规范的BNF中,->
用::=
来表示
CFG产生语言的基本方法——推导
推导:产生式产生语言的过程是从开始符号S开始,对产生式左部的非终结符反复地使用产生式:将产生式左部的非终结符替换为右部的文法符号序列(展开产生式,用标记=>表示),直到得到一个终结符序列
- 利用产生式产生句子的过程中,将产生式A→γ的右部代替文法符号序列αAβ中的A得到αγβ的过程,称αAβ直接推导出αγβ,记作:αAβ=>αγβ
- 若对于任意文法符号序列α1,α2,…αn,均α1=>α2=>…=>αn,则称此过程为零步或多步推导,记为:α1=∗>αnα1=^*>αnα1=∗>αn,其中α1=αn的情况为零步推导;若α1≠αn,即推导过程中至少使用一次产生式,则称此过程为至少一步推导,记为:α1=+>αnα1=^+>αnα1=+>αn
- 对于所有α,有α=∗>αα=^*>αα=∗>α,即推导具有自反性
- 若α=∗>βα=^*>βα=∗>β,β=∗>γβ=^*>γβ=∗>γ,则α=∗>γα=^*>γα=∗>γ,即推导具有传递性
CFL上下文无关语言
- 由CFG G所产生的语言L(G)被定义为:
- L(G)={ω∣S=+>ωandω∈T∗}L(G)=\{\omega|S=^+>\omega\ and\ \omega\in T^*\}L(G)={ω∣S=+>ω and ω∈T∗}
- L(G)称为上下文无关语言(Context Free Language, CFL),ω称为句子,若S=*>α,α∈(N∪T)*,则称α为G的一个句型
第一个是文法开始符号,最后一个是句子,其他的都是句型,但广义来说,第一个和最后一个也是句型
- 在推导过程中,若每次直接推导均替换句型中最左边的非终结符,则称为最左推导,由最左推导产生的句型被称为左句型;
- 类似的可以定义最右推导与右句型,最右推导也被称为规范推导
分析树
- 分析树是推导的图形表示,直观并且同时反映语言结构的实质和推导过程
对CFG G的句型,分析树被定义为具有下述性质的一棵树。
- (1) 根由开始符号所标记
- (2) 每个叶子由一个终结符、非终结符、或ε标记
- (3) 每个内部结点由一个非终结符标记
- (4) 若A是某内部节点的标记,且X1,X2,…,Xn是该节点从左到右所有孩子的标记,则A→X1X2…Xn是一个产生式。若A→ε,则标记为A的结点可以仅有一个标记为ε的孩子
分析树与语言和文法的关系:
- 每一直接推导(每个产生式),对应一棵仅有父子关系的子树,即产生式左部非终结符“长出”右部的孩子
- 分析树的叶子,从左到右构成G的一个句型;若叶子仅由终结符标记,则构成一个句子
语法树
- 为了仅关注句型,并且忽略推导过程,产生了语法树:
对CFG G的句型,表达式的语法树被定义为具有下述性质的一棵树:
- (1) 根与内部节点由表达式中的操作符标记;
- (2) 叶子由表达式中的操作数标记;
- (3)用于改变运算优先级和结合性的括弧,被隐含在语法树的结构中
分析树和语法树又被称为具体语法树和抽象语法树AST
二义性与二义性的消除
- 若文法G对同一句子产生不止一棵分析树,则称G是二义的
产生原因:
- 在产生句子的过程中某些直接推导有多于一种选择;
- 文法中缺少对文法符号优先级和结合性的规定;一个句子有多于一颗分析树,仅与文法和句子有关,与采用的推导方法无关(对于某些文法和句型,无论采用最左推导还是最右推导都会有歧义的)
文法二义性不能说明程序设计语言是二义的
- 程序设计语言不能二义;
- 只有当产生一个语言的所有文法都是二义的时,这个语言才被认为是二义的
二义文法不是CFG
消除文法二义的两种方法:
- 改写二义文法为非二义文法
- 规定二义文法中符号的优先级和结合性,使仅产生一颗分析树
现给出一个二义文法:
E→E+E | E*E|(E) | -E| id
改写二义文法为非二义文法
对于上述二义文法进行改写:
E → E + T | T
T → T * F | F
F →(E) | -F | id
改写二义文法的方法:
- 通过引入非终结符,使原来分辨不清的结构受到约束,从而使得对任何一个句子,仅能构造一颗分析树
一些结论:
- 新引入的非终结符,限制了每一步直接推导均有唯一选择
- 最终分析树的形状,仅与文法有关,而与推导方法无关
- 非终结符的引入,增加了推导步骤(分析树增高),从而分析树效率降低
- 越接近S的文法符号的优先级越低(如E→E+T)
- 对于A→αAβ,若a∈βa\in\betaa∈β(A在a的左边),则a具有左结合性质;若a∈αa\in\alphaa∈α(A在a的右边),则a具有右结合性质***【如E->E+T,则+具有左结合性,E->T+E,则+具有右结合性】***
关键步骤:
- 引入一个新的非终结符,增加一个子结构并提高一级优先级
- 递归非终结符在终结符左边,运算具有左结合性,否则具有右结合性
说明
- 先列出优先级,比如这里我可以说从低到高是[+] [*] [(), -, id];
- 然后列出结合性:左结合+,;右结合-;无结合id;因为有三个层次,所以需要再引入两个新变量,首先是优先级最低的,然后是次之,最后是最高的;
- 在每一个产生式中,又要根据结合性,如果是左结合的则右边应该含有终结符的标号,否则相反,就可以写出来了;当然要注意可以不含有+的问题,所以有个|T的存在*
对于“悬空”问题(即else和最近还是最远if匹配)
- 因为没有优先级区分,但是结合性应该是右结合,即else与其左边最靠近的then结合,那么只需改写如下:
原来的:
S → if C then S| if C then S else S| id := E
C → E=E | E<E | E>E
E → E+E | -E | id | n改写之后的(MS是完全匹配的意思,即含有if then else;UMS不完全匹配,即含有if then,至于then中是否嵌套,则看如下表示):
S → MS | UMS
MS → if C then MS else MS | id := E
UMS→ if C then S | if C then MS else UMS
C → E=E | E<E | E>E
E → E + T | T
T →(E) | -T | id | n
然后根据一一比对,比如对于if x<3 then if x>0 then x:=5 else x:=-5
比如对于和最远的if匹配的话,
- 先将S展开,如果是MS,则展开为第一种,但是MS展不开了(这里应该是
if x>0 then x:=5
这句话); - 如果是UMS,则展开为第二种,但是MS也展不开了,所以这种匹配不可行;
- 而和最近的if匹配的话,是可行的,且唯一确定,首先展开成UMS,S再展开成MS的第一种
规定二义文法中符号的优先级和结合性
但是二义文法具有如下优点:
- 比非二义文法容易理解
- 分析效率高,分析树低,直接推导步骤少
通过为二义文法规定优先级和结合性(YACC的方法)
修改语言的语法(表现形式被改变)
- 明确给出结束标志,如
end if
- 给表达式加括号
正规式与CFG
正规式到CFG的转换
正规式所描述的语言结构均可用CFG描述,反之不一定
识别正规语言的自动机是有限自动机,它们的特征是没有记忆功能*
识别 CFL 的自动机是下推自动机,在有限自动机的基础上增加了一个下推栈,具有简单的记忆功能*
从正规式到CFG的对应关系:
- 构造正规式的NFA
- 若0为初态,则A0A_0A0为开始符号
- 对于move(i,a)=j,引入产生式AiA_iAi→aAjaA_jaAj
- 对于move(i,ε)=j,引入产生式 Ai→AjA_i→A_jAi→Aj
- 若i是终态,则引入产生式Ai→εA_i →εAi→ε
为什么用正规式而不用CFG描述词法:
- 词法规则简单,用正规式描述已足够
- 正规式的表示比CFG更直观、简洁、易于理解
- 有限自动机的构造比下推自动机简单,且分析效率高
- 区分词法和语法,为编译器前端的模块划分提供方便
- 正规式适合描述线性结构,如标识符、关键字、注释等
- CFG适合描述具有嵌套(层次)性质的非线性结构,如不同结构的句子if-then-else、while-do等
上下文有关语言CSL
变量的声明与引用、过程调用时形参与实参的一致性检查等无法用CFG描述,所以产生了CSL(Context Sensitive Language)
CFG到CSL的文法所表示的意思都变了
CFG无法表示:
L1={ωcω|ω∈(a|b)*} (标识符声明与引用一致性的抽象)
L2={a^n b^m c^n d^m|n≥1和m≥1} (形参ab与实参cd一致性的抽象)
L3={a^nb^nc^n|n≥1} (输入n个字符,回退n个字符,加n个底线,计数问题的抽象)对文法稍加修改,得到相近的CFL:
【ω^r是ω的逆序】
L1'={ωcω^r|ω∈(a|b)*} (S→aSa|bSb|c)
L2'={a^n b^m c^m d^n|n≥1, m≥1} (S→aSd|aAd A→bAc|bc)
L2''={a^n b^n c^m d^m|n≥1, m≥1} (S→AB A→aAb|ab B→cBd|cd)
L3'={a^m b^m c^n|m, n≥1} (S→AC A→aAb|ab C→cC|c)正规式:
L3''={a^k b^m c^n|k,m,n>=1} a^+ b^+ c^+
命题:L3’不是正规集,因为构造不出可以识别L3’的DFA
- 证明:(反证)
- 假设L3’是正规集,则可构造n个状态的DFA D,它接受L3’;
- 考察D读完ε,a,aa,…,anε,a,aa,…,a^nε,a,aa,…,an,分别到达S0,S1,…,SnS0,S1,…,SnS0,S1,…,Sn,共有n+1n+1n+1个状态。
- 根据鸽巢原理,序列中至少有两个状态相同,设Si=Sj(j>i)S_i=S_j(j>i)Si=Sj(j>i),因为aibick∈L3’a^ib^ic^k∈L3’aibick∈L3’,所以存在路径aibicka^ib^ic^kaibick,但是D中也有路径ajbicka^jb^ic^kajbick,矛盾;故L3’不是正规集
形式语言与自动机
若文法G=(N,T,P,S)G=(N,T,P,S)G=(N,T,P,S)的每个产生式α→βα→βα→β中,均有α∈(N∪T)∗α∈(N∪T)^*α∈(N∪T)∗,且至少含有一个非终结符,β∈(N∪T)∗β∈(N∪T)^*β∈(N∪T)∗,则称G为0型文法
- 任何0型语言都是递归可枚举的;反之,递归可枚举集必定是一个0型语言
对0型文法施加以下第i条限制,即得到i型文法。
- G的任何产生式α→β(S→ε除外)满足|α|≤|β|
- G的任何产生式形如A→β,其中A∈N,β∈(N∪T)∗β∈(N∪T)^*β∈(N∪T)∗【对于αAβ→αγβ\alpha A\beta\to\alpha \gamma\betaαAβ→αγβ,则A只有在左边是α\alphaα,右边是β\betaβ这样的上下文才可能替换成γ\gammaγ
- G的任何产生式形如A→a或者A→aB(或者A→Ba),其中A和B∈N,a∈T
文法 | 语言 | 自动机 |
---|---|---|
短语文法(0型) | 短语结构语言 | 图灵机 |
CSG (1型) | CSL | 线性界线自动机 |
CFG (2型) | CFL | 下推自动机 |
正规文法(3型) | 正规集 | 有限自动机 |
CSG、CFG、正规式能力递减,但是能力越强的文法,其文法的设计和自动机的构造越苦难。
【编译原理系列】语法分析与上下文无关文法相关推荐
- 【编译原理】什么是上下文无关文法?
上下文无关是指,一句话的含义与其前后的内容没有或者几乎没有关系,只由自己决定,把它剪切到其他任何位置,也还是原有的意思. 例如: ... a = 0; ... 这是一个赋值语句,无论此语句的前后是什么 ...
- 编译原理之语法分析(预测分析法)
编译器之语法分析 自顶向下 上下文无关文法 语法树 NFA→CFG 预测分析法 改写CFG 原因 消除二义性 消除左递归 消除左公因子 消除空产生式 消除回路 自顶向下 上下文无关文法 CFG本质上就 ...
- 《编译原理》-3.上下文无关文法及分析
上下文无关文法及分析 3.1 分析过程 3.2 上下文无关文法 3.2.1 与正则表达式的比较 3.2.2 上下文无关文法规则的说明 3.2.3 推导及由文法定义的语言 3.3 分析树与抽象语法树 3 ...
- 上下文无关文法的分析树(Context-Free Grammar, CFG)的分析树--编译原理
上下文无关文法的分析树(Context-Free Grammar, CFG)的分析树 分析树 根节点的符号为文法开始符号S 每个内部节点都是对某个产生式A→β的应用,该节点的标号就是产生式的左部,子节 ...
- 【编译原理】构造产生如下语言的上下文无关文法各一个:
13.构造产生如下语言的上下文无关文法各一个: (1) (an bm c2m | n,m≥0 } S->AB A->ε|aA B->ε|bBcc (2) w c wR| w∈{a,b ...
- 编译原理学习笔记 5.1 翻译文法和语法制导翻译
前言 参考课上PPT内容. 该学习笔记目前仅打算个人使用. 后续会进一步整理,包括添加笔记内容,标明参考资料. 更新中... 跳过目录 目录 导言 一.翻译文法和语法制导翻译 输入文法 翻译文法 活动 ...
- 三万五千字长文!让你懂透编译原理(六)——第六章 属性文法和语法制导翻译
三万五千字长文!让你懂透编译原理(六)--第六章 属性文法和语法制导翻译 长文预警 系列文章传送门: 万字长文+独家思维导图!让你懂透编译原理(一)--第一章 引论 万字长文!让你懂透编译原理(二)- ...
- 编译原理2-Bison语法分析
ps:补上了图 实验要求 了解 Bison 基础知识和理解 Cminus-f 语法(重在了解如何将⽂法产⽣式转换为 Bison 语句) 阅读 /src/common/SyntaxTree.c ,对应头 ...
- 【计算理论】计算理论总结 ( 上下文无关文法 CFG 转为下推自动机 PDA 示例 2 ) ★★
文章目录 一.上下文无关文法 CFG 转为下推自动机 PDA 流程 二.上下文无关文法 CFG 转为下推自动机 PDA 示例 2 参考博客 : [计算理论]上下文无关语法 ( 语法组成 | 规则 | ...
- 【计算理论】计算理论总结 ( 上下文无关文法 CFG 转为下推自动机 PDA 示例 1 ) ★★
文章目录 一.上下文无关文法 CFG 转为下推自动机 PDA 流程 二.上下文无关文法 CFG 转为下推自动机 PDA 示例 1 参考博客 : [计算理论]上下文无关语法 ( 语法组成 | 规则 | ...
最新文章
- python3中的单例模式Singleton
- 谁动了我的奶酪?--java实例初始化的顺序问题
- 2019ICPC(上海) - Color Graph(二分图+状态压缩)
- 互联网岗位也存在鄙视链?我们来康康你喜欢的岗位竞争究竟有多残酷?
- Makefile编写详解--项目开发
- Polygon Mesh Processing读书笔记——1三角网格Triangle Meshes
- SQL Server里面如何检查没有释放的游标
- iOS APP中第三方APP调用自己的APP,打开文件
- 高通平台SPI驱动框架分析
- 2020张宇1000题【好题收集】【第七章:三重积分、曲线曲面积分】
- 37--8位级联加法器,并行加法器
- 倾斜摄影测量知识基础
- python查询12306余票_python自动查询12306余票并发送邮箱提醒脚本
- Squid中文权威指南-第10章 与其他Squid会话
- Linux安装jdk报错:package jdk-16.0.2-2000:16.0.2-ga.aarch64 is intended for a different architecture
- Java 常见设计模式
- 终身免费!微软数据恢复工具,界面版体验
- ios 多线程gdc_GDC 2019中的Heretic,Megacity版本,实时光线追踪以及更多新闻
- c++从零开始---文章导读
- ulua中lua代码使用反射调用c#详解
热门文章
- 图解机器学习算法 | 从入门到精通系列教程(机器学习通关指南·完结)
- 解决VS 2017/2019社区版无法登陆的方法
- 使用 SOUI 开发高 DPI 桌面应用程序
- win10 动态磁盘 linux,win10 动态磁盘 无效,win10动态磁盘不能识别
- Mysql 数据库(一)—— 初识 Mysql
- 中控考勤机重置考勤机密码方法
- 微观、宏观、精准 多视角估算数据库性能(选型、做预算不求人)
- C++ vector 初始化大小
- 项目总结 【电商后台管理系统】
- win7计算机iis,win7系统如何打开iis管理器|win7系统打开iis管理器的方法