[编译原理]Stanford斯坦福CS143第三周第五章

网课链接:https://www.bilibili.com/video/BV1NE411376V?p=20



Introduction to Parsing

很多很重要的语句并不能用正则表达式或者有限自动机来表示,例如: { ( i ) i ∣ i ≥ 0 } { \{(^i)^i|i≥0\}} {(i)ii0}。这指出了很多嵌套语句不能很好的匹配。cool语言中if语句有对应的fi语句来标识这个语句的结束,但是很多其他语言中并没有结束标识,而是隐式地结束,这些不能用正则表达式匹配。

那什么正则表达式可以表达什么样的式子呢?为什么他们不足以识别任意的嵌套语句?

举例:

下面是一个识别偶数个1的有限自动机:在这里插入图片描述
他只能识别奇偶,并不能识别对应的次数,因此他只能识别mod kk是机器上的状态数,即不能完成嵌套语句的前后匹配。

解析器(Parser)

  • 输入:来自词法分析程序(lexer)的令牌(token)序列。
  • 输出:这个程序的解析树。

举例:

cool语言:if x = y then 1 else 2 fi
Parser input:IF ID = ID THEN INT ELSE INT FI
Parser output:在这里插入图片描述

总结(lexer和parser的比较):

Phase输入输出
Lexer一连串字符(即字符串)一串令牌
Parser一串令牌解析树

注意:

  1. 有时解析树是隐式的,所以编译器永远不会构建完整的解析树。
  2. 有些编译器将Lexer和Parser两个阶段合并成一个强大的Parser。但是大部分的编译器还是将两者分开的,因为正则表达式与词法分析非常匹配,所以Parsing单独处理。

上下文无关语法(Context-Free Grammars)

我们已经知道:

  • 不是所有的令牌都是有效的程序。
  • 解析器必须区分有效和无效的令牌。

所以我们需要:

  • 一种语言来描述有效的令牌。
  • 一种来区别有效或无效令牌的算法。

与上下文无关的语法(CFG)是一种来描述这种嵌套结构的自然的表示方式,它包括:

  1. 一组终端(T)
  2. 一组非终端(N)
  3. 一个起始符号(S, S∈N)
  4. 一组Production:在这里插入图片描述
    下面是关于 N N N T T T的特定的CFG的结果:
    在这里插入图片描述

Productions

可以理解为规则,右边的项可以等价代换左边的项。 流程如下:

  1. 开始时是一个字符串,只有起始符号S。
  2. 将字符串中的所有非终端 X X X通过规则用Production的右边项替代( X − > Y 1 Y 2 . . . Y n X->Y_1Y_2...Y_n X>Y1Y2...Yn)。
  3. 重复第2步直到没有非终端N。

a 1 − > a 2 − > . . . a n a_1->a_2->...a_n a1>a2>...an可以记作 a 0 − ∗ > a n a_0-^*>a_n a0>an(在大于等于0步中,有操作可以使 a 0 a_0 a0 a n a_n an)。

Terminals

G G G是上下文无关的语法,起始符号 S S S,那么语言 T ( G ) T(G) T(G)为: { a 1 . . . a n ∣ a i ∈ T ∧ S − ∗ > a 1 . . . a n } \{a_1...a_n|a_i∈T ∧S-^*>a_1...a_n \} {a1...anaiTS>a1...an}

  • 终端是没有规则(Production)可以替换的。
  • 一旦生成,终端是永久存在的。
  • 终端应该是这种语言的令牌(token)。

举例:
cool语言:

if EXPR then EXPR else EXPR fi

可以看出非终端习惯上用大写字母表示,终端用小写字母表示。

id
可以导出EXPR->id

简单的算数表达式

举例:

在这里插入图片描述

此外,上下文无关的语法还需要:

  1. 我们目前定义的只给出了是或否的答案,还需要一个在输入中构建解析树的方法。
  2. 必须能很好地处理一些错误。
  3. 具体的CFG实现(比如bison)。

建立一个CFG十分重要:

  1. 很多语法产生相同的语言
  2. 很多工具都是语法敏感的

推导(Derivations)

一个Derivation是一系列的production:

S -> … -> … -> … -> … -> …

还可以画成树形结构:

  • 起始符号作为树的根节点
  • 对于一个Production: X − > Y 1 Y 2 . . . Y n X->Y_1Y_2...Y_n X>Y1Y2...Yn,将 Y 1 Y 2 . . . Y n Y_1Y_2...Y_n Y1Y2...Yn加入作为 X X X的孩子节点。

举例:

语法:
E − > E + E ∣ E ∗ E ∣ ( E ) ∣ i d E -> E + E | E * E | (E) | id E>E+EEE(E)id
字符串:
i d ∗ i d + i d id*id+id idid+id

推导及解析树(Parse Tree)如下:

这其实是一个left-most deriation,每一步替换最左边的非终端。

在这里插入图片描述

对应的也有right-most derivation

在这里插入图片描述
可以画出树状图得出:最左和最右边的生成是具有相同的解析树的。一个生成树可能有很多Derivation,最左和最右生成是最重要的解析器实现。

解析树的特征:

  1. 在叶子节点有终端也有非终端
  2. 对于树的叶子节点内部进行有序(从左向右)遍历,可以得到原来输入的字符串(如上图即遍历id*id+id)
  3. 解析树显示了操作符的相关性,而输入字符串并没有

模糊的上下文语法(Ambiguity)

如果某一个字符串可以用多于一种的解析树表示,那么这个语法就是模糊的。或者说,对于某个字符串,有不止一个最左或最右生成。

例如,根据上面的语法,下面两个解析树都可以表示 i d ∗ i d + i d id*id+id idid+id,则说明这种语法具有歧义。
在这里插入图片描述

处理上下文歧义(模糊)的方法

  1. 重写一种没有歧义的新语法。

例如上面的语法改成非模糊的可以写成:

E − > E ′ + E ∣ E E -> E' + E | E E>E+EE
E ′ − > i d ∗ E ′ ∣ i d ∣ ( E ) ∗ E ′ ∣ ( E ) E' -> id*E' | id | (E) * E' | (E) E>idEid(E)E(E)
这个语法可以理解为:E的作用就是可以变成无数多个E’相加,E’的作用是可以变成无数多个id或者(E)的相乘。
总而言之,这里所有的加法都要在乘法之前实现,这样在解析树中乘号的深度才比加号深。


根据这种语法,上面例子中的右边的解析树不复存在。

再举一个例子:

语法:
E − > i f   E   t h e n   E E-> if\ E\ then\ E E>if E then E
∣   i f   E   t h e n   E   e l s e   E |\ if\ E\ then\ E\ else\ E  if E then E else E
∣   O T H E R |\ OTHER  OTHER
表达式:
i f   E 1   t h e n   i f   E 2   t h e n   E 3   e l s e   E 4 if\ E_1\ then\ if\ E_2\ then\ E_3\ else\ E_4 if E1 then if E2 then E3 else E4

这个表达式可能就会存在两个解析树,第一种是 e l s e else else语句是外层 i f if if的分支,第二种是内层 i f if if的分支:

在这里插入图片描述

而我们想要的是 e l s e else else和最近的 i f if if匹配,我们可以修改语法:

E   − >   M I F / ∗ 所 有 的 t h e n 语 句 都 已 匹 配 ∗ / E\ ->\ MIF /* 所有的then语句都已匹配*/ E > MIF/then/
             ∣   U I F   / ∗ 有 些 t h e n 语 句 没 有 匹 配 ∗ / \ \ \ \ \ \ \ \ \ \ \ \ |\ UIF\ /* 有些then语句没有匹配*/              UIF /then/
M I F   − >   i f   E   t h e n   M I F   e l s e   M I F   ∣   O T H E R MIF\ ->\ if\ E\ then\ MIF\ else\ MIF\ |\ OTHER MIF > if E then MIF else MIF  OTHER
U I F   − >   i f   E   t h e n   E UIF\ ->\ if\ E\ then\ E UIF > if E then E
                 ∣   i f   E   t h e n   M I F   e l s e   U I F \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |\ if\ E\ then\ MIF\ else\ UIF                  if E then MIF else UIF

这种语法就排除了上图右侧的解析树的情况。


参考资料:

编译原理龙书

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值