这篇博客是根据自己学习龙书,因为博主习惯了英语环境,在强行从英语转化为中文的时候难免会有些不自然,请大家谅解。
配套的练习题答案可以在 https://github.com/Oh233/Dragon_book_exercise 看到。
感谢沉鱼姐姐,很多答案都是参考了她的github,虽然无缘认识,但也算是一位领路人。
正文:
一个简单的语法制导翻译器
在本章中,要先给一个编译器大致的idea,而并非一开始就专注于各种编译器过程的细节。
2.1 引言
首先快速浏览一下编译器的前端模型,也就是词法分析,语法分析,中间代码生成的这三个过程。
源程序通过词法分析器得到了一个个的词法单元,之后这些词法单元通过语法分析器被构造成了一棵抽象语法树。之后传给中间代码生成器,会产生树或者三地址形式的中间形式表达。
2.2 语法定义
我们要介绍的是一种用于描述程序设计语言语法的表示方法——“上下文无关文法”。
2.2.1 文法定义
一个上下文无关文法(context free grammar)由以下四个元素组成:
- 一个终结符号集合。终结符号是该文法所定义的语言的基本符号的集合。
- 一个非终结符号集合。每个非终结符号表示一个终结符号串的集合。
- 一个产生式集合。产生式的结构包括产生式头(左部)的非终结符号,一个箭头,和一个产生式体(右部)的由终结符号和非终结符号组成的序列。产生式主要用来描述一种非终结符号的结构体是如何被定义的,即一种非终结符号的书写方式。
- 指定一个非终结符号作为开始符号。
为了更好地理解上下文无关文法,我们在此举一个例子。
终结符号为 + - 0 1 2 3 4 5 6 7 8 9
list → list + digit
list → list - digit
list → digit
digit → 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
上述例子说明了digit是一个非终结符号,这个非终结符号digit是由数字0123456789之中的某些单一数字组成的。而list这个非终结符号,是由另一个list 加上(注意这里只是表达式的加减,不会做出结果)digit 形成。根据开始符号的定义,我们还能看到这段文法的开始符号就是 list 。
零个终结符号组成的串成为空串,记为 ϵ .
2.2.2 推导
我们可以根据文法推导所有可能生成的符号串,比如可以从开始符号出发,不断将某个非终结符号替换为该非终结符号产生式的体(右部),这样可以推导得到所有终结符号串能得到的集合。这一套集合被称为语言。
在语法分析(parsing)的过程中,我们的目的是接受一个终结符号串作为输入,找出从文法的开始符号推导出这个串的方法。如果不能从文法的开始符号推导得到该输入的终结符号串,则报告语法错误。
用上面例子中的一套定义的话,我们如果输入 9 - 5 + 2,编译器就会开始推导说这个表达式是一个list,所以没有语法错误。这个即是推导的作用。
2.2.3 语法分析树
在上述从文法的开始符号推导到给定的终结符号串的过程中,我们可以用树状结构来简化(更好的描述)我们的推导过程。我们把具有以下性质的树称作是语法分析树:
- 根节点的标号是文法的开始符号
- 每个叶子节点的编号为一个终结符号或者是空串 ϵ
- 每个内部节点的标号都是一个非终结符号。
- 如果非终结符号A是某个内部节点的标号,并且它的子节点的标号从左到右分别为 X1