文法的概念

每一种自然语言或者是编程语言都需要文法来描述,文法相当于语言学的语义分析,即分析每一句话所表示的含义,编译器需要利用文法来完成其语法分析和语义分析。

在目前编程语言领域,上下文无关文法作为程序语言的描述工具,比如a = b + c是一个合法的赋值语句。

符号和符号串的定义

每个程序都可以看成是一个“基本符号”串,如果有一个基本符号集,那么C语言等编程语言可以看成是在这个基本符号集上定义的、按照一定规则构成的一切基本符号串组成的集合。

字母表是元素的非空有穷集合,字母表中的元素称之为符号,因此字母表也称之为符号集。例如C语言中的字母表由字母、数字、关键字等组成。

符号串就是由符号集中的元素组成的序列。例如给定符号集{a,b,c},那么abcabbac就是由该符号集组成的符号串。

文法的形式化定义

文法的形式化定义可以表述为 G = ( V T , V N , P , S ) G=(V_T,V_N,P,S) G=(VT,VN,P,S) ,其中:

  • V T V_T VT 为终结符集合
  • V N V_N VN为非终结符集合
  • P P P 为产生式集合
  • S S S 为开始符号

何为终结符?在编程语言中,终结符可以理解为不能再进行拆分的基本符号,例如C语言中的ifelsewhile关键字,也可以是一个变量名、数字。

和终结符相反,非终结符就是可以再进行拆分的符号,它可以推出其它的语法成分。如果要对一个非终结符进行语法分析,就需要对它需要进行递归分析。在C语言的if-else文法中,if或者else后面跟着的代码块可以看成是非终结符。

一个文法含有一个或多个产生式,产生式描述了将终结符集合和非终结符集合组合成串的方法。其一般形式为 α → β α→β αβ,其中:

  • α α α称之为产生式的左部,并且 α ∈ ( V T ∪ V N ) + α∈(V_T∪V_N)^+ α(VTVN)+ ,表示至少要包含 ( V T ∪ V N ) (V_T∪V_N) (VTVN)中的一个元素。
  • β β β称之为产生式的右部,并且 β ∈ ( V T ∪ V N ) ∗ β∈(V_T∪V_N)^* β(VTVN),表示可以包含零个或多个 ( V T ∪ V N ) (V_T∪V_N) (VTVN)中的元素。

对于文法产生式当中的符号,我们约定终结符为:

  • 英文小写字母,如abc
  • 运算符,如+*
  • 标点符号,如逗号,括号等
  • 数字
  • 粗体字符串,如ifelse
  • 希腊字母ε为空串

非终结符约定为:

  • 英文大写字母,如ABC等。其中字母S通常表示为开始符号
  • 小写、斜体的名字,如exprstmt
  • 代表程序构造的大写字母。如E(表达式)、T(项)和F(因子)
  • 字母表中排在后面的大写字母(如X、Y、Z)表示文法符号(即终结符或非终结符)
  • 字母表中排在后面的小写字母(主要是u、v、…、z)表示终结符号串(包括空串)
  • 小写希腊字母,如αβγ,表示文法符号串(包括空串)

如非特别说明,第一个产生式的左部就是开始符号。


文法的分类

文法分为0型、1型、2型、3型四种,它们之间的差别在于对产生式施加的不同限制,形似于数据库不同范式之间的差异。我们在判断时,一般先核对该文法是否是3型文法,如果不是再核对是否是2型文法,依次往下推。

下面给出各个文法的定义

0型文法

当文法 G = ( V T , V N , P , S ) G=(V_T,V_N,P,S) G=(VT,VN,P,S) 的每个产生式 α → β α→β αβ 满足以下条件:

  • α ∈ ( V T ∪ V N ) ∗ α∈(V_T∪V_N)^* α(VTVN)
  • β ∈ ( V T ∪ V N ) ∗ β∈(V_T∪V_N)^* β(VTVN)
  • 至少含有一个非终结符

则称文法 G G G 为0型文法。0型文法又称短语文法,任何0型语言都是递归可枚举的,反过来也成立。

1型文法

文法 G = ( V T , V N , P , S ) G=(V_T,V_N,P,S) G=(VT,VN,P,S) 的每个产生式在 α → β α→β αβ 满足0型文法的要求外,还满足以下要求:

  • ∣ β ∣ > = ∣ α ∣ |β|>=|α| β>=α(绝对值符号代表集合中元素的个数)
  • S → ε S→ε Sε 除外

则称文法 G G G 为1型文法,又称上下文有关文法

例如文法 G = ( V T , V N , P , S ) G=(V_T,V_N,P,S) G=(VT,VN,P,S) V N = { S , B , E } V_N=\{S,B,E\} VN={S,B,E} V T = { a , b , e } V_T=\{a,b,e\} VT={a,b,e}的产生式为:
S → a S B E S→aSBE SaSBE
S → a B E S→aBE SaBE
E B → B E EB→BE EBBE
a B → a b aB→ab aBab
b B → b b bB→bb bBbb
b E → b e bE→be bEbe
e E → e e eE→ee eEee

因为每个产生式都满足右部元素数量大于等于左部数量,并且没有产生式为 S → ε S→ε Sε ,所以文法 G G G 是一个1型文法。

2型文法

文法 G = ( V T , V N , P , S ) G=(V_T,V_N,P,S) G=(VT,VN,P,S) 的每个产生式 α → β α→β αβ 在满足1型文法的要求外,还满足以下要求:

  • α α α 仅仅是一个非终结符
  • β ∈ ( V T ∪ V N ) ∗ β∈(V_T∪V_N)^* β(VTVN)

则称此文法是2型文法,又称上下文无关文法

例如文法 G = ( V T , V N , P , S ) G=(V_T,V_N,P,S) G=(VT,VN,P,S) V N = { S } V_N=\{S\} VN={S} V T = { 0 , 1 } V_T=\{0,1\} VT={0,1},产生式为:
S → 0 S 1 ∣ 01 S→0S1|01 S0S101(等价于两个产生式: S → 0 S 1 S→0S1 S0S1 S → 01 S→01 S01,符号|含义为“或”)
该文法就是一个2型文法。

3型文法

文法 G = ( V T , V N , P , S ) G=(V_T,V_N,P,S) G=(VT,VN,P,S) 的每个产生式满足2型文法外,还满足以下要求:

  • 形如 A → a A→a Aa A → a B A→aB AaB
  • A A A B B B都是非终结符
  • a ∈ V T ∗ a∈V_T^* aVT a a a为非终结符的零个或多个元素)

则称文法 G G G 为3型文法,又称正规文法


语法树

语法树又称语法分析树或分析树。
给定文法 G = ( V T , V N , P , S ) G=(V_T,V_N,P,S) G=(VT,VN,P,S) ,对于 G G G 的任何句型都能够构造出与之相关的语法树,这颗树满足4个条件:

  • 每个结点都有一个标记,这个标记属于 V T V_T VT 或者 V N V_N VN
  • 根的标记是 S S S
  • 若一个结点 A A A 至少有一个子结点,那么 A ∈ V N A∈V_N AVN
  • 如果结点 n n n 的直接子节点从左到右的顺序为 n 1 , n 2 , n 3 , . . . , n k n_1,n_2,n_3,...,n_k n1,n2,n3,...,nk,其标记分别为 A 1 , A 2 , A 3 , . . . , A k A_1,A_2,A_3,...,A_k A1,A2,A3,...,Ak,那么产生式 A → A 1 A 2 A 3 . . . A k A→A_1A_2A_3...A_k AA1A2A3...Ak 一定是文法 G G G 产生式集合 P P P 中的元素。

语法树表示了在推导过程中采取了哪个产生式和使用在了哪个非终结符上,它并没有表明采用的产生式的顺序
根据语法树定义,我们可以画出文法 G G G 的语法树。

例题:给出文法 G = ( { S , A } , { a , b } , P , S ) G=(\{S,A\},\{a,b\},P,S) G=({S,A},{a,b},P,S),其中 P P P 包含:
S → a A S S→aAS SaAS
A → S b A A→SbA ASbA
A → S S A→SS ASS
S → a S→a Sa
A → b a A→ba Aba

上述文法可以有三种推导方式:

  • S ⇒ a A S ⇒ a A a ⇒ a S b A a ⇒ a S b b a a ⇒ a a b b a a S⇒aAS⇒aAa⇒aSbAa⇒aSbbaa⇒aabbaa SaASaAaaSbAaaSbbaaaabbaa
    该推导总是选择最右边的非终结符,并找出以该非终结符为左部的产生式进行递归推导,该推导方式为最右推导,也被称之为规范推导
  • S ⇒ a A S ⇒ a S b A S ⇒ a a b A S ⇒ a a b b a S ⇒ a a b b a a S⇒aAS⇒aSbAS⇒aabAS⇒aabbaS⇒aabbaa SaASaSbASaabASaabbaSaabbaa
    该推导总是选择最左边的非终结符,并找出以该非终结符为左部的产生式进行递归推导,该推导方式为最左推导
  • S ⇒ a A S ⇒ a S b A S ⇒ a S b A a ⇒ a a b A a ⇒ a a b b a a S⇒aAS⇒aSbAS⇒aSbAa⇒aabAa⇒aabbaa SaASaSbASaSbAaaabAaaabbaa
    该推导则是根据情况选择不同的产生式来替换非终结符

上述三种推导方式得到的语法树都为:
在这里插入图片描述
如果一个文法存在某个句子对应两棵语法树,那么则称该文法是二义的。根据现有理论,无法判断一个文法是否是二义的,因为这个问题是递归不可解的。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐