前章再叙
解释程序不生成目标代码,编译程序生成目标代码。
对于语法树的推导和规约过程:推导属于长(zhang)枝,规约属于剪枝。
LR
语法分析中有一种自下而上的方法:LR法
自下而上意味着它从输入(通常是词法分析器产生的记号流)的底部开始构建语法树,并向上移动,直到形成完整的语法树。LR代表从左到右读取输入,并按照从右到左的顺序产生派生。它相对于自上而下的方法,如LL,更为强大,因为它可以分析更大的语法子集。
Bison
Bison是一个著名的解析器生成器,经常被用于生成LR解析器。当给定一个语法规则描述(通常为Yacc格式)时,Bison会生成一个C程序,该程序是对应的LR解析器。因此,通过Bison,开发人员可以方便地为特定的语言定义创建对应的解析器。
前后端
单趟扫描没有前后端。多趟扫描:以中间代码为中心分前端和后端。后端与目标机相关。
- 对于多趟扫描,我们通常将编译器的任务划分为两个主要部分:
- 前端:前端主要与编程语言有关,它负责解析源代码,进行语法和语义分析,并生成中间代码。
- 后端:后端则是与目标平台(如特定的处理器架构)相关的部分。它取得前端生成的中间代码,进行进一步的优化,并生成最终的机器码。
通过这种方式,例如,一个编译器的前端可以为多个不同的目标平台共用,而针对每个目标平台都有一个专门的后端来处理中间代码并生成相应的机器码。这为编译器的设计和维护提供了便利,因为当添加对新语言特性的支持时,只需修改前端;而要为新的目标平台生成代码时,只需添加或修改后端。
问题--形式化描述--计算机解决方案设计‘’
这是解决计算问题的三个步骤。首先,我们有一个实际的问题。然后,我们需要将这个问题形式化,即用数学或逻辑等形式语言来描述它。最后,我们设计一个计算机解决方案来解决这个形式化的问题。
自编译,编译的编译
任何一种语言都提供了一个特别的思考角度
第二章 文法和语言
语言的不同的定义
语言与源程序有关,是源程序的集合。但有无限多个,我们借助具体的表达:Grammer文法。
课前思考:计算机高级程序语言有哪些一般性特性?
语言概述
1. 语言是由句子组成的集合
当我们谈论“语言”,我们通常指的是一组按照某种规则组织的句子或表达式。例如,英语、中文、Python、Java 都是语言。不论是自然语言还是编程语言,它们都是由一系列句子或表达式组成的。
2. 语法、语义、语用
这三个概念用于描述和定义任何语言的重要特性。
-
语法 (Syntax): 定义了句子或表达式的结构或形式。它是一组规则,定义了什么样的字符组合是有效的,什么样的组合是无效的。例如,在英语中,“我是大学生”是一个语法正确的句子,而“我大学生是”可能就不是。
-
语义 (Semantics): 是关于句子或表达式的意义的。当我们说一个句子是“有意义的”,我们是指它在语义上是有意义的。例如,“太阳是冷的”在语法上是正确的,但在语义上是错误的(至少在我们的现实世界中)。
-
语用 (Pragmatics): 关心的是句子或表达式在实际上下文中的使用。它涉及到语境、说话者的意图等。例如,“你能把窗户关上吗?”在语用上可能是一个请求,尽管字面上它是一个问题。
语言的实例若在语法上是正确的,其相关联的意义可从两个观点来看,其一是该句子的创立者所想要表示的意义,另一是接收者所检验到的意义。这两个意义并非总是一样的,前者称为语言的语义,后者是其语用意义。幽默、双关语和谜语就是利用这两方面意义间的差异。
3. 对字符做运算
在编程语境中,我们经常对字符或字符串进行运算。这可能包括连接、裁剪、搜索、替换等。例如,在许多编程语言中,我们可以使用 +
运算符来连接两个字符串。
2.1 形式语言基础
基本概念
一、字母表和符号串
1.字母表:符号的非空有限集合 例:å={a,b,c}
2.符号:字母表中的元素 例: a,b,c
3.符号串:符号的有穷序列 例:a, aa, ac, abc,..
特别地,空符号串:无任何符号的符号串(ε)
4.符号串集合:由符号串构成的集合。
5.符号串相等:若x、y是集合上的两个符号串,则x=y
iff(当且仅当)组成x的每一个符号和组成y的每一个符号依次相等。
空串和空集是两个概念
符号串的联接:保持联接的有序性,不同序列不一样。
空串和字符的联接结果是字符本身
-
空串与空集:
-
空串:在形式语言中,空串通常表示为ε或λ,它是一个长度为0的字符串。换句话说,它没有任何字符,但仍然被认为是一个有效的字符串。
-
空集:在数学中,空集表示为∅,它是一个没有元素的集合。所以说,空串与空集是两个不同的概念。空串是一个没有字符的字符串,而空集是一个没有元素的集合。
-
-
符号串的联接:
联接是指将两个或多个字符串或符号串按照顺序连接在一起。例如,考虑两个字符串A="hello"和B="world",那么A和B的联接就是"hello"+"world"="helloworld"。请注意,联接是有顺序的,即"hello"+"world"与"world"+"hello"是两个不同的结果。
-
空串与字符的联接:
当你将空串与任何其他字符串或字符联接时,结果仍然是原始的字符串或字符。这是因为空串本身没有内容。例如,考虑一个字符C="a"和一个空串ε,那么C和ε的联接就是"a"+ε="a"。此处的结果仍然是原始的字符"a"。
正则闭包和星闭包
正则闭包:所有元素能够组成的所有符号串。
星闭包:所有元素能够组成的所有符号串+空串。
二者都是在集合的基础上提出的
首先,我们来理解闭包这个概念。在数学和计算机科学中,闭包经常指一个操作对于一个集合内的所有元素都是有效的,并且结果也在这个集合内。对于正则表达式而言,闭包有两种常见的形式:正则闭包和星闭包。
-
正则闭包: 当我们说一个集合的“正则闭包”,我们指的是由这个集合的元素可以组合成的所有可能的字符串。例如,如果我们有一个集合 {a, b},其正则闭包包括如下字符串:a, b, aa, ab, ba, bb, aaa, aab, aba, abb, ...(可以无限组合下去)。
-
星闭包: 星闭包与正则闭包非常相似,但它还包括一个额外的元素:空字符串(通常表示为ε或λ)。使用上面的集合 {a, b} 为例,其星闭包为:ε, a, b, aa, ab, ba, bb, aaa, aab, aba, abb, ...。
总结一下,两者的区别是星闭包包括了空字符串,而正则闭包不包括。这两种闭包在正则表达式和自动机理论中是非常基础的概念。例如,在正则表达式中,使用星号(*)表示一个模式出现的次数为0次或多次,这其实就是星闭包的应用。
联接
"联接"在形式语言和自动机理论中通常是指字符串或序列的串联。在集合的上下文中,这意味着将一个元素与另一个元素串在一起以形成一个新的组合。这与数学上常用的集合操作不同,因为我们现在是在元素级别而不是集合级别进行操作。
字符串联接同样注意顺序!
A^3:在字母表A上的所有长度等于3的字符串的集合
A*:在字母表A上所有字符串的集合
在集合和字符串的上下文中,可以认为闭包是一种自联接的结果。这种“自联接”意味着集合中的元素不断与其自身组合,生成新的元素,直到满足某些条件(例如,在正则闭包中,直到生成所有可能的字符串)。这种概念是类似的,但请注意它们仍然是在不同的上下文中应用的。
2.2 文法的直观理解:定义语言的很好的形式化工具
1. 什么是文法?
在编译器理论中,文法是一种形式化的方法,用于描述程序设计语言的语法。它是一组产生式规则,每个规则描述了如何从一个符号(或称为非终结符)推导出一个符号串。符号串可以是非终结符、终结符或二者的组合。
所谓文法是在形式上对句子结构的定义与描述,而未涉及语义问题。
举个例子就明白辣:<谓语>等称为“非终结符”,"我"称为终结符。
2. 推导与产生式
在文法中,使用产生式规则进行推导是基本操作。例如,有这样一个产生式规则:
<句子> -> <主题> <谓语>
这意味着在推导过程中,我们可以将 <句子>
替换为 <主题> <谓语>
。这样的替换就是一次“推导”。从<句子>多步推导成具体的句子就是我们的目标。
3. 从开始符号到句子
通常,文法有一个特定的非终结符作为其开始符号。推导的目的是从开始符号开始,逐步使用产生式规则,最终得到一个只含有终结符的符号串。推导成功表示:这个符号串就是这个文法中的句子。
例如,考虑以下简单文法:
<句子> -> <名词> <动词>
<名词> -> "猫" | "狗"
<动词> -> "跑" | "跳"
从 <句子>
开始,我们可以推导出如下句子:“猫跑”、“猫跳”、“狗跑”、“狗跳”。
4. 语法树
语法树是句子的图形表示,展示了如何使用产生式规则从开始符号推导出该句子。它是一种树形结构,其中每个内部节点代表一个非终结符,每个叶节点代表一个终结符。
对于句子“猫跑”,其对应的语法树为:
<句子>
/ \
<名词> <动词>
| |
"猫" "跑"
这棵树清晰地展示了从 <句子>
开始,如何使用产生式规则得到句子“猫跑”。
文法和推导是编译器前端的基石,特别是在语法分析阶段。它们提供了一种方法来正式描述和理解程序设计语言的结构和语法。而语法树为编译器提供了一种内部数据结构,用于表示源代码的结构,为后续的语义分析、优化和代码生成阶段提供基础。
2.3 文法,启动!
文法的定义
首先,我们从文法的定义开始。文法G=(V_N,V_T,P,Z)通常由四个组成部分定义:
在产生式中,终结符只出现在右边,或者说在产生的结果中。
非终结符必须至少出现在产生式的左边,可以出现在右边。
通过产生式,非终结符可以被展开成其它的非终结符或终结符,从而描述了某种语法结构或模式。
一个非终结符如果没有在任何产生式的左边出现,那么它在该文法中是没有意义的,因为我们无法知道它如何被展开或解释。所以,为了文法有意义,每一个非终结符都必须至少在一个产生式的左边出现。
文法实战
作者没有老师的PPT,真蠢啊!
1. 无符号整数
文法定义:
S -> SD | D
D -> 0 | 1 | 2 | ... | 9
-
D
代表单个数字。这是基本的构建块,表示一个整数可以是任意的单个数字。 -
S
可以被看作是数字序列。考虑这样的问题:“一个数字序列是什么?”答案是,它可以是一个单独的数字,或者是一个数字紧跟着一个数字序列。这正是产生式S -> SD
所代表的。这也允许我们生成任意长度的数字序列。
这个文法描述了一个无符号整数。以18为例进行推导:
S => SD => S8 => D8 => 18
然后作者发现,推导的时候需要先转到相同符号数量的形式。以18为例,先转到SD,与18都是两位。再逐个转换到最终值,转化D这个最好转化的,再变化高位,基本上就是从易到难。
2. 有符号整数
然后如果要定义有符号整数的话,只需在无符号整数的基础上增加一个符号部分。这可以通过增加以下产生式来完成:
S -> S | +S | -S
现在,S不仅可以是一个数字或数字序列,还可以是一个正数或负数。例如,要推导出“-18”,我们首先从S开始,然后使用产生式S -> -S
得到-S
,然后再按照无符号整数的推导规则得到“-18”。有符号先推出符号!
3. 奇数
对于奇数的定义,作者在课堂后与老师探讨,确定了最终的答案。
首先定义最高位O,不能是0:
O -> 1 | 2 | ... | 9
再定义S,这个S和上面无符号整数的S定义相同,作为中间数:
S -> SD | D
D -> 0 | 1 | 2 | ... | 9
然后定义M,这个M只能是奇数,即:
M -> 1 | 3 | 5 | 7 | 9
将这些组合在一起,为奇数定义一个入口点。假设N
为入口点,则:
N -> OSM | OM | M
这里的N
表示一个奇数。我们可以看到,一个奇数可能只有一个数字(只有M
)、可能有两个数字(O
和M
,如19)或可能有多个数字(如135,这是通过OSM
表示的)。