编译原理(二)——语法分析(一)

版权声明:本文为原创文章,未经博主允许不得用于商业用途。

语法分析

上下文无关文法(CFG)

1.1 基本定义

CFG包含如下四个组成部分:

  • 终结符号:组成串的基本符号(词法单元名,id,运算符)
  • 非终结符号:表示串的集合的语法符号(如expr,stmt)
  • 开始符号:某个被指定的非终结符(如expr)
  • 产生式:定义了使用非终结符和终结符狗构造串的方法。
    • 形式:头(左)部 → \rightarrow 体(右)部
    • 头部是一个非终结符,右部是一个符号串
1.2 例子(c语言产生式)

在这里插入图片描述

其中 e x p r e s s i o n expression expression表示表达式, t e r m term term表示术语(保留字,关键字)。

经过简化,此文法可以表示成:,其中’|'为文法描述符,不是文法符号。

1.3 推导

推导可以看作是对产生式的解读,如果产生式 A → γ A\rightarrow \gamma Aγ,则 α A β ⇒ α γ β \alpha A\beta\Rightarrow \alpha \gamma\beta αAβαγβ表示“通过一步推导出”。因此推导可以看作是按照产生式规则替换。同理,定义 ⇒ ∗ \overset{*}{\Rightarrow} 表示经过零步或多步推导出。

在进行推导时,如果每次都解析最左(右)边的非终结符号,则称为最左(右)推导

  • 句型:一个文法可以推导出的所有串的集合
  • 句子:不包含非终结符号的句型
  • 语言:所有句子的集合
1.4 语法分析树

推导过程可以使用树状结构表示,其中:

  • 根结点的标号是文法的开始符号
  • 每个叶子结点的标号是非终结符号、终结符号或ε
  • 每个内部结点的标号是非终结符号
  • 每个内部结点表示某个产生式的一次应用
    • 结点的标号为产生式头,其子结点从左到右是产生式的体

一棵语法分析树可以对应多个推导序列,不过只具有一种最左(右)推导(即前序遍历和后序遍历唯一)。

1.5 上下文无关文法和正则表达式

上下文无关文法比正则表达式的表达能力更强:即CFG可以对两个个体的计数(最多两个个体)而RE(NFA)不能。

例如 { a n b n ∣ n ≥ 1 } \{a^nb^n|n\geq 1\} {anbnn1}(括号匹配)
  • CFG: S → a S b ∣ a b S\rightarrow aSb|ab SaSbab
  • 正则表达式:反证法,若可以表示,若存在一个具有2n个状态的NFA可以表示此语言,则对于 a n + 1 b n + 1 a^{n+1}b^{n+1} an+1bn+1,必有一个状态 s 0 s_0 s0有一条收到a时指向之前k个状态的边,因此其可以接受 a n + k b n + 1 a^{n+k}b^{n+1} an+kbn+1,不在此语言中。矛盾。

设计文法

消除二义性

对于一些语法,同一个句型可以对应多个语法分析树,如典型的if-else-then语法:

文法1:

s t m t → i f   e x p r   t h e n   s t m t   ∣   i f   e x p r   t h e n   s t m t   e l s e   s t m t   ∣   o t h e r stmt\rightarrow \bold{if}\ expr\ \bold{then}\ stmt\ |\ \bold{if}\ expr\ \bold{then}\ stmt\ \bold{else}\ stmt\ |\ other stmtif expr then stmt  if expr then stmt else stmt  other

对于 i f   E 1   t h e n   i f   E 2   t h e n   S 1   e l s e   S 2 if\ E_1\ then\ if\ E_2\ then\ S_1\ else\ S_2 if E1 then if E2 then S1 else S2有两种解读,即 S 2 S_2 S2对应的层次可以是外层if语句,也可以是内层if语句。

可以通过规定else和最近的未匹配then匹配消除二义性,体现在文法层面如下:

文法2:

s t m t → m a t c h e d _ s t m t   ∣   o p e n _ s t m t stmt\rightarrow matched\_stmt\ |\ open\_stmt stmtmatched_stmt  open_stmt

m a t c h _ s t m t → i f   e x p r   t h e n   m a t c h e d _ s t m t   e l s e   m a t c h e d _ s t m t   ∣   o t h e r match\_stmt\rightarrow \bold{if}\ expr\ \bold{then}\ matched\_stmt\ \bold{else}\ matched\_stmt\ |\ other match_stmtif expr then matched_stmt else matched_stmt  other

o p e n _ s t m t → i f   e x p r   t h e n   s t m t   ∣   i f   e x p r   t h e n   m a t c h e d _ s t m t   e l s e   o p e n _ s t m t open\_stmt\rightarrow \bold{if}\ expr\ \bold{then}\ stmt\ |\ \bold{if}\ expr\ \bold{then}\ matched\_stmt\ \bold{else}\ open\_stmt open_stmtif expr then stmt  if expr then matched_stmt else open_stmt

消除左递归
  • 左递归:文法中存在非终结符A使得 A ⇒ + A α A\overset{+}{\Rightarrow}A\alpha A+Aα
  • 立即左递归:文法中存在非终结符A使得 A → A α A\rightarrow A\alpha AAα

自顶向下的语法分析无法处理左递归,因为在DFS时可以无限替换下去。

可以通过替换法消除左递归:

  • 立即左递归变为立即右递归:
    • 输入: A → A α ∣ β A\rightarrow A\alpha|\beta AAαβ
    • 转化为: A → β A ′ A\rightarrow \beta A' AβA A ′ → α A ′ ∣ ϵ A'\rightarrow \alpha A'|\epsilon AαAϵ
  • 多步左递归:
    • 算法思路:按照顺序替换产生式,直到立即左递归出现后,消除立即左递归。(相当于求了产生关系的闭包)
    • 在这里插入图片描述
提取左公因子

在CFG分析句型时,每次都通过查看下一个符号选择产生式,因此如果两个产生式具有相同的前缀时,则无法判断应该选择哪一个。

因此需要提取产生是的左公因子(相同前缀),并且将公因子转化为新的产生式消除相同前缀。

  • 原语法: A → α β 1 ∣ α β 2 A\rightarrow \alpha \beta_1|\alpha\beta_2 Aαβ1αβ2
  • 提取左公因子后: A → α A ′ , A ′ → β 1 ∣ β 2 A\rightarrow \alpha A',A'\rightarrow \beta_1|\beta_2 AαA,Aβ1β2

自顶向下的语法分析

自顶向下语法分析按照先根次序深度优先的创建节点(对应最左推导)建立语法分析树,一次读入一个字符,知道完成整个输入串的解析。

在这里插入图片描述

此算法类似贪心算法,显然当遇到一个错误时,不一定是语法错误,也可能是之前的产生式选择错误,因此可以通过回溯改良算法。

FIRST和FOLLOW

通常在选择产生式时使用FIRST和FOLLOW两个函数。

  • F I R S T ( α ) FIRST(\alpha) FIRST(α)定义为可以从 α \alpha α推导出的首符号的集合。
    • α \alpha α为终结符,则加入 α \alpha α
    • α \alpha α为非终结符,且 α → β 1 β 2 . . . β k \alpha \rightarrow \beta_1\beta_2...\beta_k αβ1β2...βk
      • θ ∈ F I R S T ( β i ) , ϵ ∈ F I R S T ( β 1 ) , F I R S T ( β 2 ) , . . . , F I R S T ( β i − 1 ) \theta\in FIRST(\beta_i),\epsilon\in FIRST(\beta_1),FIRST(\beta_2),...,FIRST(\beta_{i-1}) θFIRST(βi),ϵFIRST(β1),FIRST(β2),...,FIRST(βi1),加入 θ \theta θ
      • ϵ ∈ F I R S T ( β 1 ) , F I R S T ( β 2 ) , . . . , F I R S T ( β k ) \epsilon\in FIRST(\beta_1),FIRST(\beta_2),...,FIRST(\beta_{k}) ϵFIRST(β1),FIRST(β2),...,FIRST(βk),加入 ϵ \epsilon ϵ
    • α \alpha α为非终结符,且 α → ϵ \alpha \rightarrow \epsilon αϵ,加入 ϵ \epsilon ϵ
  • F I R S T ( α 1 α 2 . . . α n ) FIRST(\alpha_1\alpha_2...\alpha_n) FIRST(α1α2...αn)计算如下:
    • 加入 F I R S T ( α 1 ) FIRST(\alpha_1) FIRST(α1)中所有非 ϵ \epsilon ϵ符号
    • ϵ ∉ F I R S T ( α i ) , ϵ ∈ F I R S T ( α 1 ) , F I R S T ( α 2 ) , . . . , F I R S T ( α i − 1 ) \epsilon\notin FIRST(\alpha_i),\epsilon\in FIRST(\alpha_1),FIRST(\alpha_2),...,FIRST(\alpha_{i-1}) ϵ/FIRST(αi),ϵFIRST(α1),FIRST(α2),...,FIRST(αi1),加入 F I R S T ( β i ) FIRST(\beta_i) FIRST(βi)
    • ϵ ∈ F I R S T ( α 1 ) , F I R S T ( α 2 ) , . . . , F I R S T ( α n ) \epsilon\in FIRST(\alpha_1),FIRST(\alpha_2),...,FIRST(\alpha_{n}) ϵFIRST(α1),FIRST(α2),...,FIRST(αn),加入 ϵ \epsilon ϵ
  • F O L L O W ( α ) FOLLOW(\alpha) FOLLOW(α)定义了可以跟随在 α \alpha α右边的终结符号集合
    • 例如: S → a α b β S\rightarrow a\alpha b\beta Saαbβ,则终结符b在 F O L L O W ( α ) FOLLOW(\alpha) FOLLOW(α)
    • 计算:将右端结束标记$加入FOLLOW(B)中,保证非空
      • 存在 S → α B β S\rightarrow \alpha B\beta SαBβ,则将 F I R S T ( β ) FIRST(\beta) FIRST(β)中的非 ϵ \epsilon ϵ符号加入
      • 存在 A → α B   o r   A → α B β , ϵ ∈ F I R S T ( β ) A\rightarrow \alpha B\ or\ A\rightarrow \alpha B\beta,\epsilon\in FIRST(\beta) AαB or AαBβ,ϵFIRST(β),则将 F O L L O W ( A ) FOLLOW(A) FOLLOW(A)中所有符号加入
LL(1)文法

LL(1)文法中的LL和1分别代表从左向右扫描输入字符、最左推导、每一步只需向前看一个输入字符。

对于任意一个产生式 A → α ∣ β A\rightarrow \alpha |\beta Aαβ,满足如下两个条件:

  • F I R S T ( α ) ∩ F I R S T ( β ) = Φ FIRST(\alpha)\cap FIRST(\beta)=\Phi FIRST(α)FIRST(β)=Φ
  • ϵ ∈ F I R S T ( β ) , F I R S T ( α ) ∩ F O L L O W ( A ) = Φ \epsilon\in FIRST(\beta),FIRST(\alpha)\cap FOLLOW(A)=\Phi ϵFIRST(β),FIRST(α)FOLLOW(A)=Φ,反之亦然

对于满足上述条件的LL(1)文法,可以构造出不需要回溯的递归下降语法分析器。

预测分析表

为了避免重复计算,为语言构造预测分析表M,其中行表示非终结符号,列表示输入符号(终结符号),每个单元记录非终结符是否可以通过产生式推导出终结符。

构造算法:

  • 对于文法G中的每个产生式 A → α A\rightarrow\alpha Aα
    • 对于 F I R S T ( α ) FIRST(\alpha) FIRST(α)中的每个终结符号a,将 A → α A\rightarrow \alpha Aα写入 M [ A , a ] M[A,a] M[A,a]单元中
    • 如果 ϵ ∈ F i r s t ( α ) \epsilon\in First(\alpha) ϵFirst(α),对于 F O L L O W ( A ) FOLLOW(A) FOLLOW(A)中的每个终结符号b,将 A → α A\rightarrow \alpha Aα写入 M [ A , b ] M[A,b] M[A,b]单元中
  • 算法执行后,在空白单元格填入error

由于LL(1)语法不存在二义性,因此在执行递归算法时对于每个输入符号可以根据M唯一的选择产生式。

特别的,如果出现冲突,则说明G不满足LL(1)的条件,即具有二义性。

非递归预测分析

由于LL(1)文法时最左推导的,因此每次分析时只需记住尚未分析的产生式右侧部分即可。

可以通过一个栈实现预测分析。具体过程如下:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值