2020-编译原理-Parser2-上下文无关文法

上下文无关文法(CFG, Context-Free Grammer, 上下文无关文法)

  1. LL(1)可以完成手写分析器
  2. LR(1)可以更不容易出现细节问题
  3. Bison工具是本实验使用的工具。

1. 上下文无关文法(CFG)定义

  1. 上下文无关文法GG是一个四元组G=(T,N,P,S)G=(T,N,P,S):
    1. T是终结符号 Terminal集合, 对应于词法分析器产生的词法单元
    2. N是非终结符号 Non-terminal集合
    3. P是产生式 Production集合

ANα(TN)A \in N \rightarrow \alpha \in (T \cup N)*

  1. 头部/左部(Head)AA:
    1. 单个非终结符,必须只有一个,是命名为上下文无关文法的原因。
    2. 如果有多个则不符合上下文无关文法,会被成为上下文有关文法,但是几乎无法处理。
  2. 体部/右部(Body)α\alpha:
    1. 终结符与非终结符构成的串
    2. 也可以是空串ϵ\epsilon
  3. SS为开始(Start)符号
  4. 要求SNS \in N且唯一。

1.1. 上下文无关文法示例

  1. 一个符号要么是终结符号,要么是非终结符号
  2. 终结符号表示到此为止,无法再进行替换

G=({S},{(,)},P,S)SSSS(S)S()\begin{array}{l} G = (\{S\}, \{(, )\}, P, S) \\ S \rightarrow SS \\ S \rightarrow (S) \\ S \rightarrow () \\ \end{array}

上面的文法表示任意嵌套的所有匹配好的括号串

G=({S},{a,b},P,S)SaSbSϵ\begin{array}{l} G = (\{S\}, \{a, b\}, P, S) \\ S \rightarrow aSb \\ S \rightarrow \epsilon \\ \end{array}

可以有很多的情况

1.2. 条件语句文法

  1. 条件语句文法中包含悬空(Dangling)-else文法的问
  2. 这样的文法是有问题,我们进一步分析才可以,OTHER作为终结符。

1.3. 约定

约定: 如果没有明确指定, 第一个产生式的头部就是开始符号,用来避免写如上例子中的一些描述

1.4. 关于终结符号的约定

下述符号是终结符号:(通常的约定)

  1. 在字母表里排在前面的小写字母,比如a、b、c。
  2. 运算符号,比如+、*等。
  3. 标点符号,比如括号、逗号等。
  4. 数字,0、1、… 9。
  5. 黑体字符串,比如id或if。每个这样的字符串表示一个终结符号。

1.5. 关于非终结符号的约定

下述符号是非终结符号:

  1. 在字母表中排在前面的大写字母,比如A、B、C。
  2. 字母S。它出现时通常表示开始符号。
  3. 小写、斜体的名字,比如expr或stmt.

2. 语义

  1. 上下文无关文法GG定义了一个语言L(G)L(G)
  2. 语言是的集合

3. 推导(Derivation)的定义

3.1. 表达式文法

EEE+EEE(E)idE \rightarrow -E|E + E | E ∗ E | (E) | id

  1. 推导即是将某个产生式的左边替换成它的右边
  2. 每一步推导需要选择替换哪个非终结符号,以及使用哪个产生式

EE(E)(E+E)(id+E)(id+id)E \Rightarrow -E \Rightarrow −(E) \Rightarrow −(E+E) \Rightarrow −(id+E) \Rightarrow −(id+id)

EE:E+(id+E):E(id+E):\begin{array}{l} E \Rightarrow −E : 经过一步推导得出 \\ E \xRightarrow{+} −(id + E):经过一步或多步推导得出 \\ E \xRightarrow{*} −(id +E):经过零步或多步推导得出 \\ \end{array}

EE(E)(E+E)(E+id)(id+id)E \Rightarrow -E \Rightarrow −(E) \Rightarrow −(E+E) \Rightarrow −(E+id) \Rightarrow −(id+id)

3.2. Definition (Sentential Form,句型)

如果SαS \xRightarrow{*} \alpha,且α(TN)\alpha \in ( T \cup N)^*,则称α\alpha是文法G的一个句型(包含非终结符)

EEE+EEE(E)idEE(E)(E+E)(id+E)(id+id)\begin{array}{l} E \rightarrow -E | E + E | E ∗ E | (E) | id \\ E \Rightarrow -E \Rightarrow −(E) \Rightarrow −(E+E) \Rightarrow −(id+E) \Rightarrow −(id+id) \end{array}

3.3. Definition (Sentence,句子)

  1. 如果SwS \xRightarrow{*} w,且wTw \in T^*,则称w是文法G的一个句子(没有非终结符了)
  2. 句子就是这个语言中的串

4. Definition (文法G生成的语言L(G))

文法F的语言L(G)是它能推导出的所有句子构成的集合。

wL(G)Sww \in L(G) \Leftrightarrow S \xRightarrow{*} w

4.1. 关于文法G的两个基本问题

  1. Membership问题:给定字符串xT,xL(G)x \in T^*, x \in L(G)?字符串可不可以由文法推理得到
  2. L(G)究竟是什么?

4.1.1. 问题一:Membership问题

  1. 给定字符串xT,xL(G)x \in T^*, x \in L(G),(即检查x是否符合文法G)
  2. 这就是语法分析器的任务:为输入的词法单元流寻找推导、构建语法分析树, 或者报错

  1. 根节点是文法G的起始符号
  2. 叶子节点是输入的词法单元流
  3. 常用的语法分析器以自顶向下自底向上的方式构建中间部分

4.1.2. 问题二:L(G) 是什么?

这是程序设计语言设计者需要考虑的问题

4.1.2.1. 根据文法G推导语言L(G)

例子一

SSSS(S)S()SϵL(G)={}\begin{array}{l} S \rightarrow SS \\ S \rightarrow (S) \\ S \rightarrow () \\ S \rightarrow \epsilon \\ L(G) = \{良匹配括号串\} \\ \end{array}

例子二

SaSbSϵL(G)=anbnn0\begin{array}{l} S \rightarrow aSb \\ S \rightarrow \epsilon \\ L(G) = {a^nb^n|n \geq 0 } \\ \end{array}

4.1.2.2. 根据语言L(G)来推导文法G

  1. 目标生成:字母表=a,b\sum = {a, b}上的所有回文串(Palindrome)构成的语言

SaSaSbSbSaSbSϵSaSabSbabϵ\begin{array}{l} S \rightarrow aSa \\ S \rightarrow bSb \\ S \rightarrow a \\ S \rightarrow b \\ S \rightarrow ϵ \\ S \rightarrow aSa|bSb|a|b|\epsilon \\ \end{array}

  1. 目标生成:bnamb2nn0,m0{b^na^mb^{2n}|n \geq 0, m \geq 0}

SbSbbAAaAϵ\begin{array}{l} S \rightarrow bSbb|A \\ A \rightarrow aA|\epsilon \\ \end{array}

  1. 目标生成:{x{a,b}xa,b}\{x\in \{a, b\}^* | x 中a,b个数相同 \}
    1. 证明:a可以是空串
    2. 证明:a以a开头,那么后面肯定能找到一个b保证被分为了aVbV并且V中的ab数量均相同,以b开头类似。

VaVbVbVaVϵ\begin{array}{l} V \rightarrow aVbV | bVaV | \epsilon \\ \end{array}

  1. 目标生成:{x{a,b}xa,b}\{x\in \{a, b\}^* | x 中a,b个数不同 \}
    1. T表示a多一个或者更多
    2. U表示b多一个或者更多
    3. UT不能同时出现,和V去组合即可
    4. 证明

STUTVaTVaVUVbUVbVVaVbVbVaVϵ\begin{array}{l} S \rightarrow T | U \\ T \rightarrow VaT |VaV \\ U \rightarrow VbU | VbV \\ V \rightarrow aVbV | bVaV | \epsilon \\ \end{array}

4.2. 顺序语句、条件语句、打印语句

上图中的L是顺序语句

5. L-System(不考)

L-System:这不是上下文无关文法, 但精神高度一致

variables:ABconstants:+start:Arules:(ABAB),(BA+B+A)angles:60\begin{array}{l} variables: A B \\ constants: + - \\ start: A \\ rules:(A \rightarrow B-A-B),(B \rightarrow A+B+A) \\ angles:60' \\ \end{array}

  1. A,BA,B:向前移动并画线
  2. +:左转
  3. -:右转
  4. 每一步都并行地应用所有规则

variables:XYconstants:F+start:FXrules:(XX+YF+),(YFXY)angles:90\begin{array}{l} variables: X Y constants: F + - start: FX rules:(X \rightarrow X+YF+),(Y \rightarrow -FX-Y) angles:90' \end{array}

  1. FF:向前移动并画线
  2. +:右转
  3. -:左转
  4. X:仅用于展开,在作画时被忽略
  5. 每一步都并行地应用所有规则

6. 最左(leftmost) 推导与最右(rightmost) 推导

EE+EEE(E)idElmElm(E)lm(E+E)lm(id+E)lm(id+id)\begin{array}{l} E \rightarrow E + E | E * E | (E) | id \\ E \xRightarrow[lm]{} -E \xRightarrow[lm]{} -(E) \xRightarrow[lm]{} -(E+E) \xRightarrow[lm]{} -(id + E) \xRightarrow[lm]{} -(id+id) \end{array}

  1. ElmEE \xRightarrow[lm]{} -E:经过一步最左推导得出
  2. Elm+(id+E)E \xRightarrow[lm]{+} -(id + E):经过一步或多步最左推导得出
  3. Elm(id+E)E \xRightarrow[lm]{*} -(id + E):经过零步或多步最左推导得出
  4. 最左推导是有非终结符优先选择最左侧的进行推导
  5. 最右推导是有非终结符有限选择最右侧的进行推导

ErmErm(E)rm(E+E)rm(E+id)rm(id+id)E \xRightarrow[rm]{} -E \xRightarrow[rm]{} -(E) \xRightarrow[rm]{} -(E+E) \xRightarrow[rm]{} -(E + id) \xRightarrow[rm]{} -(id+id)

6.1. Definition (Left-sentential Form,最左句型)

  1. 如果SlmαS \xRightarrow[lm]{*} \alpha, 并且α(TN)\alpha \in (T \cup N)^*,则称α\alpha是文法G的一个最左句型

ElmElm(E)lm(E+E)lm(id+E)lm(id+id)E \xRightarrow[lm]{} -E \xRightarrow[lm]{} -(E) \xRightarrow[lm]{} -(E+E) \xRightarrow[lm]{} -(id + E) \xRightarrow[lm]{} -(id+id)

6.2. Definition (Right-sentential Form,最右句型)

  1. 如果SrmαS \xRightarrow[rm]{*} \alpha, 并且α(TN)\alpha \in (T \cup N)^*,则称α\alpha是文法G的一个最右句型

ErmErm(E)rm(E+E)rm(id+E)rm(id+id)E \xRightarrow[rm]{} -E \xRightarrow[rm]{} -(E) \xRightarrow[rm]{} -(E+E) \xRightarrow[rm]{} -(id + E) \xRightarrow[rm]{} -(id+id)

7. 语法分析树

  1. 语法分析树是静态的, 它不关心动态的推导顺序

  1. 一棵语法分析树对应多个推导
  2. 但是, 一棵语法分析树与最左(最右) 推导一一对应

7.1. 二义性引入

  1. 1 - 2 - 3的语法树的两种不同表达形式
  2. 以下的两棵语法树是不同的,生成出来的目标代码也是不同的
  3. 这个文法是有问题的,这个文法具有二义性,我们需要通过修改文法避开二义性问题。
  4. 语法没有二义性的描述,L(G)本身只是一个串的集合。

7.2. Definition (二义性(Ambiguous) 文法)

  1. 如果L(G) 中的某个句子有一个以上语法树/最左推导/最右推导,则文法G是二义性的。
  2. 1 + 2 * 3的语法树

7.3. 悬空-else

8. 二义性文法

  1. 不同的语法分析树产生不同的语义
  2. 所有语法分析器都要求文法是无二义性

  1. Q:如何识别二义性文法?这是不可判定的问题,是指没有通用的算法,可以判断任意的文法G
  2. Q:如何消除文法的二义性?

8.1. 消除文法二义性

  1. 四则运算均是左结合的
  2. 优先级: 括号最先, 先乘除后加减
  3. 二义性表达式文法以相同的方式处理所有的算术运算符
  4. 要消除二义性, 需要区别对待不同的运算符
  5. 将运算的"先后" 顺序信息编码到语法树的"层次"结构中

EE+Eid\begin{array}{l} E \rightarrow E + E | id \\ \end{array}

8.2. 左结合文法

EE+TTid\begin{array}{l} E \rightarrow E + T \\ T \rightarrow id \\ \end{array}

8.3. 右结合文法

ET+ETid\begin{array}{l} E \rightarrow T + E \\ T \rightarrow id \\ \end{array}

8.4. 使用左(右)递归实现左(右)结合

8.5. 括号最先, 先乘后加文法

乘号更靠近叶子节点

EE+EEE(E)idEE+TTTTFFF(E)id\begin{array}{l} E \rightarrow E + E|E*E|(E)|id \\ E \rightarrow E + T|T \\ T \rightarrow T * F | F\\ F \rightarrow (E)|id \end{array}

8.6. Summary

EE+EEEEEE/E(E)idnumberEE+TETTTTFT/FFF(E)idnumber\begin{array}{l} E \rightarrow E + E|E - E|E*E|E/E|(E)|id|number \\ E \rightarrow E + T | E - T|T \\ T \rightarrow T * F | T/F | F \\ F \rightarrow (E) | id | number \\ \end{array}

  1. 无二义性的表达式文法
    1. E:表达式(expression)
    2. T:项(term)
    3. F:因子(factor)
  2. 将运算的"先后"顺序信息编码到语法树的"层次"结构中

8.7. IF-Then-Else问题

每个else与最近的尚未匹配的then匹配

  1. 基本思想: then与else之间的语句必须是"已匹配的"
  2. 证明两件事情:证明部分不作为重点
    1. L(G)=L(G)L(G) = L(G')
    2. GG'是无二义性的

8.7.1. L(G)=L(G)L(G) = L(G')证明过程

L(G)L(G)\begin{array}{l} L(G') \subseteq L(G) 简单容易证明\\ \end{array}

文法是递归的,我们可以使用数学归纳法,对推导步骤进行归纳

L(G)L(G)xL(G)xL(G)stmt...x\begin{array}{l} L(G) \subseteq L(G') \\ x \in L(G) \rightarrow x \in L(G') \\ stmt \rightarrow ... \rightarrow x \\ \end{array}

  1. 只要G中展开一步,G’中都有相应的对应即证明了L(G)L(G)L(G) \subseteq L(G')

8.7.2. GG' 是无二义性的

  1. 每个句子对应的语法分析树是唯一的
  2. 只需证明: 每个非终结符的展开方式是唯一的

L(matched_stmt)L(open_stmt)=L(matched_stmt1)L(matched_stmt2)=L(open_stmt1)L(open_stmt2)=\begin{array}{l} L(matched\_stmt) \cap L(open\_stmt) = \emptyset \\ L(matched\_stmt_1) \cap L(matched\_stmt_2) = \emptyset \\ L(open\_stmt_1) \cap L(open\_stmt_2) = \emptyset \\ \end{array}

上面的下标代表子句

  1. Q:为什么不使用优雅、强大的正则表达式描述程序设计语言的语法?
  2. A:正则表达式的表达能力严格弱于上下文无关文法

每个正则表达式r对应的语言L® 都可以使用上下文无关文法来描述

r=(ab)abbr=(a|b)^∗abb

  1. 此外, 若δ(Ai,ϵ)=Aj\delta(A_i,\epsilon) = A_j,则添加AiAjA_i \rightarrow A_j

SaSbSϵL=anbnn0\begin{array}{l} S \rightarrow aSb \\ S \rightarrow \epsilon \\ L = {a^nb^n|n \geq 0} \\ \end{array}

  1. 该语言无法使用正则表达式来描述
  2. 定理:L={anbnn0}L = \{a^nb^n | n ≥ 0\} 无法使用正则表达式描述
  3. 反证法
    1. 假设存在正则表达式r:L(r)=LL(r) = L
    2. 则存在有限状态自动机D®:L(D(r))=LL(D(r)) = L; 设其状态数为k
    3. 考虑输入am(m>k)a^m(m>k)

  1. D(r)D(r)也能接受a{i+j}bi,矛盾
  2. Pumping Lemma for Regular Languages:L=anbnn0L = {a^nb^n | n \geq 0}
  3. Pumping Lemma for Context-free Languages:L=anbncnn0L = {a^nb^nc^n | n \geq 0}
  4. 只考虑无二义性的文法这意味着,每个句子对应唯一的一棵语法分析树

2020-编译原理-Parser2-上下文无关文法
https://spricoder.github.io/2021/01/16/2020-Compilation-Principle/2020-Compilation-Principle-Parser2-%E4%B8%8A%E4%B8%8B%E6%96%87%E6%97%A0%E5%85%B3%E6%96%87%E6%B3%95/
作者
SpriCoder
发布于
2021年1月16日
许可协议