形式语言与编译 12 自上而下的语法分析

语法分析之自上而下

一、上下文无关文法与程序结构

这种文法的 格式(模式) \(A->\alpha\) \(\alpha\)是变量与常量组成的字符串。

化成乔姆斯基范式(CNF)和格林巴赫范式(GNF)是为了写程序做准备

通常我们程序句子可以划分如下:

  • 说明、定义语句
  • 普通语句
    • 表达式语句以及赋值语句
    • 控制语句
    • 函数语句

要是一个文法能够把这两大类(四小类)语句能够描述清楚,就可以说其能够描述程序。

顺序语句文法:

控制语句文法:

二、语法分析的任务与分类

语法分析可以作为主函数,按需调用词法分析器。不用一下子全部把Token流写出来。

符号表是一种数据结构,可以读写。供以后使用。

归约:从终结符串(句子、常量串) 一直到变量S

三、自上而下分析面临的问题

文法的左递归;

回溯;

替换符的顺序会影响接受的语言; \(A->ab|a;A->a|ab\)

难报告出错位置

引起上面这些问题的不确定性有3种:

选择哪一个非终结符、选择哪一个产生式、处理句子时读入几个单词符号(Token )

  1. 仅有产生式选择是非确定的

    采用穷尽+回溯的方法

需要对上面的文法做一定的限制

消除左递归 略

消除回溯;

图示情况1可以唯一确定

这种是比较好区分的,通过查看产生式体的第一个字符

这种情况下就不容易区别哪一个产生式了,因为产生式体的第一个字符相同

解决办法:提取左公因子(实际就是对原来产生式进行修改,公共前缀只出现在单个产生式体中)

情况三

\(N_1,N_2\) 一直往下推,直到推出常量,这个句型串的第一个字符是常量符号就可以啦(可以理解,情况二和情况三是情况一的变式和拓展)

推出\(\epsilon\) 的时候,我们当然就得向后看了,比如上面的 \(N_1,N_2\) 推出\(\epsilon\) ,我们就需要看 \(\alpha_1,\alpha_2\)

\(A ~ ->~\alpha_1,~\alpha_2\) 这些要看透,一直到露出其第一个常量符号

\(FIRST(\alpha)\) 表示的是 \(\alpha\) 的首字母集合

为什么对于选择产生式,要求\(FIRST(\alpha)\) ??

因为对于变量 \(A->\alpha_1|\alpha_2\) 这两个产生式体,我们是选择 \(\alpha_1\) 呢还是选择 \(\alpha_2\) 呢 由于我们有这样的定义 \(FIRST(\alpha_1) \cap FIRST(\alpha_2) = \Phi\) 所以呢,对于输入符号a,如果 \(a\in FIRST(\alpha_1)\) ,则 \(a\notin FIRST(\alpha_2)\) ,所以可以选定产生式 \(A-> \alpha_1\)

等一下,我们上面说消除回溯,那么消除回溯的目的是什么??

Ans: 让我们产生式 \(A->\alpha_1|\alpha_2|\alpha_3|…\) 这样的产生式体推导的首字符集不会相交!! 即\(FIRST(\alpha_1)\cap FIRST(\alpha_2) \cap FIRST(\alpha_3) \cap … =\Phi\)

四、TopDown 之 递归下降法

核心就是 不含左递归\(FIRST(\alpha_1 )\cap FIRST(\alpha_2)\)

CFG与扩展的BNF

五、TopDown之预测分析法

可以把递归改为非递归 ,时空转换,从而提高效率

输出: 正确与否 分析树

和下推自动机类似 PDA的功能与预测分析表功能一样

分析表的格式 :一个\(2*2\) 的表格

行代表 变量;列代表常量+1(1是额外加入的终结符 # )

表格内容是 产生式 或者 error出错处理程序

预测分析表实际工作过程 与我们的PDA是一样的;产生式输出 就指导了一个最右推导

这个表的用途:句子的规范推导可以在这样一张表的指导下最终推导出正确结论

工作动作示例:

**对待变量 : 左部出栈,右部反序压栈 **

那么上面这张表怎么来的 怎么填的??

对于 \(A->\ X_1X_2X_3\) 如果\(X_1\)是常量\(a\) ,那么就把\(A->\ X_1X_2X_3\) 填入\([A,a]\)

如果\(X_1\) 是变量,那么就把\(FIRST(X_1X_2X_3)\) 所有可能的常量符展开\(x,y,z\) ,把 \(A->\ X_1X_2X_3\) 填入到 \([A,x],[A,y],[A,z]\) ;

如果\(\ X_1X_2X_3\)\(\epsilon\) , \(\epsilon \in FIRST(\ X_1X_2X_3)\) ,那么把\(A->\ X_1X_2X_3\) ,加入到 \([A,b_1],[A,b_2]\) 其中 \(b_1,b_2 \in FOLLOW(A)\)


什么时候使用 \(A->\epsilon\) ?? 当前字符(c)属于 \(FOLLOW(A)\)时,我们使用 \(A->\epsilon\)

\(FOLLOW(A)\) 表示 A的后面跟什么?

注意细节:\(FIRST(\alpha)\)\(\alpha\) 是字符串;

\(FOLLOW(A)\) 的A是变量。

从起始变量开始观察每一个含有A变量的产生式 ,取A后面跟这的常量。然后组成集合。和\(FIRST(\alpha)\) 一样,也是常量符号组成的集合

第四条可以这样理解: \(N->S\beta|other\) 这样的话,就一定有\(FIRST(S\beta) \subseteq FIRST(N)\)

注意到 \(X->Y_1Y_2Y_3\)\(FIRST(x)\) 则先求 \(FIRST(Y_1)\) ,若\(Y_1\) 没有\(\epsilon\) ,则\(FIRST(x) = FIRST(Y_1)\) 结束;若 \(Y_1\)\(\epsilon\) ,则把踢掉\(\epsilon\) 的其他常量符加入 \(FIRST(x)\) ,然后继续求\(FIRST(Y_2)\) ,对\(FIRST(Y_2)\) 也做类似分析,看看是否含有\(\epsilon\)


FOLLOW

对3可以做如下理解:

本来有 \(A->\alpha B\) 我们要求\(FOLLOW(B)\) ,本来对 \(\gamma_1A\gamma_2\) 我们有替换,\(\gamma_1\alpha B \gamma_2\) 则我们要求,发现\(FOLLOW(A) \subseteq FOLLOW(B)\)

预测分析表有一部分需要使用first与follow集合,现在我们回去再看预测分析表怎么填

\(first(\alpha)\) 把一个变量彻底暴露出所有可能的常量集合

分析表的构造还是通过文法得到的 输入:文法 输出:分析表

六、TopDown之LL(1)

LL(1)分析法

第一个L表示从左到右扫描输入串;

第二个L表示 最左推导

(1) 表示分析查表时,只向前看一个符号

LL(1)文法:分析表每个格子要么没有产生式,要么只有一条产生式

使用 LL(1)文法 一定可以实现不带回溯且自上而下 的语法分析

判断G(S)文法是LL(1)文法,就采用两个条件即可:

  1. $FIRST(\alpha_1) \cap FIRST(\alpha_2) \neq \Phi $
  2. \(FIRST(\alpha_1) \cap FOLLOW(A) \neq \Phi\)

要是不是 \(\Phi\) 的话,都意味着会有两条产生式仍然面临回溯。也就是说只要有交集,那么就不会是LL(1)文法。

面对这种非 LL(1)的文法,两条产生式在一个单元格。我们强行删掉一条。(强删一条也就是约定,既然约定了,也就面临抉择,不会产生回溯,选择是唯一的)

posted @ 2020-07-07 17:35  _Sandman  阅读(324)  评论(0编辑  收藏  举报