语法分析
词法分析:
- 字母是元素,组成字符串,记号的集合,线性结构,以字符流为输入
语法分析:
- 记号是元素,组成句子, 句子的集合,树结构,以记号流为输入
语法的双重含意:
- 语法规则:上下文无关文法(子集-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 = + > ω a n d ω ∈ 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\beta a∈β(A在a的左边),则a具有左结合性质;若 a ∈ α a\in\alpha a∈α(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为初态,则 A 0 A_0 A0为开始符号
- 对于move(i,a)=j,引入产生式 A i A_i Ai→ a A j aA_j aAj
- 对于move(i,ε)=j,引入产生式 A i → A j A_i→A_j Ai→Aj
- 若i是终态,则引入产生式 A i → ε 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 , a a , … , a n ε,a,aa,…,a^n ε,a,aa,…,an,分别到达 S 0 , S 1 , … , S n S0,S1,…,Sn S0,S1,…,Sn,共有 n + 1 n+1 n+1个状态。
- 根据鸽巢原理,序列中至少有两个状态相同,设 S i = S j ( j > i ) S_i=S_j(j>i) Si=Sj(j>i),因为 a i b i c k ∈ L 3 ’ a^ib^ic^k∈L3’ aibick∈L3’,所以存在路径 a i b i c k a^ib^ic^k aibick,但是D中也有路径 a j b i c k a^jb^ic^k ajbick,矛盾;故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、正规式能力递减,但是能力越强的文法,其文法的设计和自动机的构造越苦难。