朱娜斐编译原理复习笔记-北京工业大学软件学院

朱娜婓编译原理学习笔记

说明

  • 笔记大部分内容来自参考资料[1], 看了B站上中科大华保健老师的编译原理课视频(参考资料[2]),补充完善了DFA的代码表示、Hopcroft 算法、文法重写、LL(1)算法、LR算法等内容

  • 有许多知识是结合了自己的理解进行整理,所以可能会有错误之处

  • 再往后因为时间问题就有点烂尾了...

  • 文章末尾有北京工业大学2019年软件学院编译原理的考题回忆及朱娜斐老师划分的考点(recoded by @杰哥)

Keyword

  • 中科大华宝健编译原理学习笔记
  • 北京工业大学朱娜婓编译原理学习笔记
  • 2019年北京工业大学软件学院编译原理期末考试试卷回忆版

什么是编译器

什么是编译器

编译器是一个程序,核心功能是把源代码翻译成目标代码

编译器的工作解释

源代码-->编译器的静态计算-->目标程序-->计算机的动态计算-->计算结果

静态计算 是指编译器只根据程序文本静态地分析(如做报错分析、优化分析),而不是真的拿 CPU 去执行

计算机 可能是一个 x86 的物理器(如对应 C 语言),也可能是 JVM java 虚拟机(如对应 java)。

编译器和解释器的比较

解释器也是一类处理程序的程序

区别在于:

  • 编译器:输入源代码,输出一个可执行程序,但不去执行

    (存放在磁盘上等待被加载到内存中执行)

  • 解释器:输入源代码,直接输出执行结果

    其实 JVM 就是一个解释器,而不是一个单纯的编译器。输入 java 字节码 bytecode ,然后直接输出执行结果,而不是输出汇编代码。

编译器简史

第一个编译器是Fortran语言的编译器

该编译器给计算机科学的发展产生了巨大的影响:

  • 理论上:算法、数据结构、形式语言与自动机
  • 实践上:软件工程、体系结构等
  • 编译器架构

编译器内部结构

简述

编译器具有非常模块化的高层结构

编译器可以看做多个阶段构成的“流水线” 结构

编译器规模庞大,拆分模块容易实现和维护

一种没有优化的编译器结构

7346071-589ddf961bef38d2.png
一种没有优化的编译器结构

一种更复杂的编译器结构

7346071-c19fe0a2a913a1c7.png
一种更复杂的编译器结构

编译器通常会被划分为两个部分(如下图):

  • 前端:源代码生成中间代码,和源代码有关
  • 后端:中间代码生成目标代码并优化,和目标代码有关
  • 两者以抽象语法树 AST(Abstract Syntax Tree) 作为连接数据

[图片上传失败...(image-188606-1560422078662)]

一个简单的例子

背景一:现在我们设计一个叫做 Sum 的语言,特别简单,仅仅支持两种语法。第一是整形数字 n ,第二是加法表达式 e1 + e2 。举几个例子:

  • 3
  • 5 + 6
  • 7 + 8 + 9 (加法要满足左结合性,即先计算 7 + 8
  • 7 + (8 + 9)
  • 但不支持 7 + 8 * 9 ,Sum 语言中没有乘法

背景二:有一个栈式计算机 Stack (后面会再次讲到),其中有一个操作数栈,只支持两条指令,push nadd 。之所以选择栈式计算机,第一是因为简单,第二是因为 JVM 就是采用了这种形式。其指令的详细解释例子如下:

  • push 3 将 3 压栈
  • push 4 将 4 压栈
  • add 将 3 和 4 出栈,然后做加法得到 7 ,再将 7 压栈。即将栈顶的两个元素都出栈,做加法,将结果再压栈

有了上述两个背景之后,接下来的任务是:将程序 1 + 2 + 3 编译到栈式计算机 Stack 。

第一个阶段是词法分析,先不管其中的原理是什么,总之词法分析会将 1 + 2 + 3 拆分为 1 + 2 + 3 这 5 个部分。(后面会提到词法分析的原理就是用正则表达式匹配)

第二阶段是语法分析,就是将词法分析拆分出来的内容,分析是否满足 Sum 语言的语法要求,即 ne1 + e2 这种语法。

第三个阶段是语法树构造(有时算在语法分析阶段里) ,经过某些计算之后(可以看出是按中序遍历生成了二叉树),得到的抽象语法树如下图:

[图片上传失败...(image-d8b081-1560422078662)]

第四个阶段是根据抽象语法树做代码生成。首先,要满足加法的左结合性,对树进行遍历的时候就要优先遍历左子树,即后序遍历(左右根)

在遍历树节点的过程中,如果遇到整数 n 就生成一条 push n 指令,如果遇到 + 就生成一条 add 指令。

接下来详细看一下这棵树的遍历过程:

  • 第一步要访问的节点是 1 ,生成 push 1 ,将 1 压栈
  • 第二步要访问的节点是 2 ,生成 push 2 ,将 2 压栈
  • 第三步要访问的节点是 + ,生成 add ,将 1 2 出栈,计算加法得到 3 ,将 3 压栈 (这里即体现了加法的左结合性)
  • 第四步要访问的节点是 3 ,生成 push 3 ,将 3 压栈
  • 第五步要访问的节点是 + ,生成 add ,将 3 3 出栈,计算加法得到 6 ,将 6 压栈,完成

词法分析

词法分析简介

简介

从编译器内部结构得知,执行编译的第一个阶段就是词法分析。

词法分析的任务:将字符流转为记号流

字符流即源程序代码,记号流即编译器内部定义的数据结构、编码所识别出的词法单元

词法分析即将源程序代码与编译器内部定义的数据结构相对应

通俗来说,就是将源代码进行最细粒度的拆解,例如上面的例子将 1 + 2 + 3 拆分为 1 + 2 + 3 一样

一个例子

[图片上传失败...(image-58705d-1560422078662)]

如上图,从源代码到记号流(单词流)。

词法分析器会将源程序根据关键字、标识符(变量)、括号、引号、运算符、值(整数、字符串)等这些要素,将其从左到右拆分为若干个记号(单词),其中会忽略空格和换行等。上图中记号流输出的含义:

  • IF 关键字
  • LPAREN RPAREN 左右括号
  • INDENT(x) 即标识符(变量),有一个属性 x ,表示变量名
  • GR>
  • INT(t)int 类型值,属性是 5
  • 其他同理……
  • 最后红色的 EOF 是结束符

根据上面的例子,可以总结出 token 其实有固定的形式,就可以定义其数据结构,如下图(本文中高级语言的示例,默认情况下都是 C 语言)

[图片上传失败...(image-61f347-1560422078662)]

理解了例子,定义了数据,接下来就要去探寻词法分析的实现算法,第一,手工构造;第二,自动生成 。

词法分析的手工构造法

手工构造即手写一个词法分析器,例如 GCC LLVM ,优点是利于掌控和优化细节,缺点是工作量大、易出错。手工构造法主要用到“转移图”这种数据结构,下面举两个例子说明。

[图片上传失败...(image-fafc05-1560422078662)]

上图的转移图模型,即可识别逻辑运算符,如 <= < <> >= > 。识别到第一个字符,就继续往下做分支判断,直到返回一个确定的运算符。

图中的 * 即一次回溯,即将当前的这个字符再返回到词法分析器重新进行分析。

例如 >1 ,读到了 1 这个字符时,此时已经确定了运算符是 > ,而当前的 1 并不是运算符的一部分,因此将 1 再重新返回到词法分析器中重新进行分析。

[图片上传失败...(image-8d0828-1560422078662)]

上图是标识符(变量)的转移图模型,以及伪代码。其中 * 即一次回溯,跟上面一样。

关键字(如 class if for 等)是一种特殊的标识符,也满足标识符的规则。

要识别关键字,有两种解决方案:

  • 继续扩展转移图的分支,识别到关键字走不通的分支逻辑,最后识别出关键字。
  • (关键字表算法) 先识别所有的合法标识符,然后从已经识别出来的标识符中查找关键字。此时需要为该语言所有的关键字维护一个哈希表,如果数据结构合理(完美哈希),查询可以在 O(1) 复杂度内完成。

词法分析的自动生成技术

简介

所谓自动生成技术,就是有这样现成的工具(如 lex flex jlex),输入一些声明式的规范,即可自动生成一个词法分析器。优点当然是简单快速,缺点就是无法控制细节。而这里的“声明式规范”,就是我们常见的正则表达式。下文的内容,就是如何用程序去解析正则表达式,如果你之前看过关于“正则表达式 原理”这类的文章,可能早就有了解了。

先说一下自动生成技术的几个阶段,专业术语后面都有解释:

  • 正则表达式 -> NFA(Thompson 算法)
  • NFA -> DFA(子集构造算法)
  • (DFA的优化( Hopcroft 最小化算法))
  • DFA -> 词法分析代码,即完成自动生成
正则表达式
概念解释

正则表达式是一种数学上的概念,

首先它要有一个完整的字符集 Σ = {...} 要能涵盖程序所有的关键字、变量名、数字、运算符、括号、引号、特殊符号等

  • 如 C 语言的这个字符集就是ASC 编码,即 256 个字符
  • 如 java 的字符集就是 unicode 编码,可能几万甚至十几万个字符集(因为 java 的变量名称并不仅限于英文、中文也可以作为变量)

然后只有以下几个基本的逻辑:

  • 空串 \varepsilon 是正则表达式
  • 对于字符集中的任意单个字符是正则表达式
  • 如果M和N是正则表达式,则以下也是正则表达式:
    • 选择 M|N 两者取并集
    • 连接 MN 是正则表达式,两者相连
    • 闭包 M*={ \varepsilon​ ,M,MM,MMM...} 称为“闭包”(和程序的闭包不一样),即可以有 0 或者若干个 M
  • 以上随机组合,都是正则表达式,例如 a|(bc*)

这就是正则表达式的定义,而现代正则表达式这么多的语法,例如 [a-b] ? + 等,都是后来扩展出的语法糖,即对基本规则的一种简写方式。

7346071-3e8e400462e49767.jpg
正则表达式的形式表示
正则表达式例子
  • C语言中的关键字,例如if,while等,如何用正则表达式表示?

    就是用 if , while 表示,因为 i 是字符集(C语言对应ASC字符集)的元素, f 是字符集中的元素,所以 它们连接形成if 是正则表达式

    同理可说明为什么其它关键字是正则表达式

  • C语言中的标识符:以字母或下划线开头,后跟零个或多个字母、数字或下划
    线。如何用正则表达式表示?

    (a|b|c|...|z|A|B|...|Z|_ )(a|b|c|...|z|A|B|...|Z|_ |0|1|2...|9)*

    如果用语法糖来表示的话就是[a-zA-Z_] [a-zA-Z_0-9]*

语法糖
7346071-63554f402cb9db45.jpg
语法糖
有限状态自动机 FA

也称“有穷自动机”,是一种数学模型。

简单理解,就是输入一个字符串,输出这个字符串是否满足某个规则(true / false)。

FA(有限状态自动机) 实质上是带有边和节点的有向图。

1.1 编译器概述.flv 1.2 编译器结构.flv 1.3 编译器实例.flv 2.1 词法分析的任务.flv 2.2.1 词法分析器的手工构造1.flv 2.2.2 词法分析器的手工构造2.flv 2.2.3 词法分析器的手工构造3.flv 2.3.1 正则表达式1.flv 2.3.2 正则表达式2.flv 2.3.3 正则表达式3.flv 2.3.4 正则表达式4.flv 2.4.1 有限状态自动机1.flv 2.4.2 有限状态自动机2.flv 3.1.1 RE转换成NFA:Thompson算法1.flv 3.1.2 RE转换成NFA:Thompson算法2.flv 3.1.3 RE转换成NFA:Thompson算法3.flv 3.2.1 NFA转换成DFA:子集构造算法1.flv 3.2.2 NFA转换成DFA:子集构造算法2.flv 3.2.3 NFA转换成DFA:子集构造算法3.flv 3.2.4 NFA转换成DFA:子集构造算法4.flv 3.3.1 DFA的最小化:Hopcroft算法1.flv 3.3.2 DFA的最小化:Hopcroft算法2.flv 3.3.3 DFA的最小化:Hopcroft算法3.flv 3.4.1 从DFA生成分析算法1.flv 3.4.2 从DFA生成分析算法2.flv 3.4.3 从DFA生成分析算法3.flv 4.1.1 语法分析的任务1.flv 4.1.2 语法分析的任务2.flv 4.1.3 语法分析的任务3.flv 4.2.1 上下文无关文法和推导1.flv 4.2.2 上下文无关文法和推导2.flv 4.2.3 上下文无关文法和推导3.flv 4.2.4 上下文无关文法和推导4.flv 4.2.5 上下文无关文法和推导5.flv 4.3.1 分析树和二义性文法1.flv 4.3.2 分析树和二义性文法2.flv 4.3.3 分析树和二义性文法3.flv 4.3.4 分析树和二义性文法4.flv 4.4.1 自顶向下分析1.flv 4.4.2 自顶向下分析2.flv 4.4.3 自顶向下分析3.flv 4.4.4 自顶向下分析4.flv 4.5.1 递归下降分析算法1.flv 4.5.2 递归下降分析算法2.flv 4.5.3 递归下降分析算法3.flv 4.5.4 递归下降分析算法4.flv 5.1.1 LL(1)分析算法1.flv 5.1.2 LL(1)分析算法2.flv 5.1.3 LL(1)分析算法3.flv 5.1.4 LL(1)分析算法4.flv 5.1.5 LL(1)分析算法5.flv 5.1.6 LL(1)分析算法6.flv 5.1.7 LL(1)分析算法7.flv 5.1.8 LL(1)分析算法8.flv 5.2 LL(1)分析的冲突处理.flv 5.3.1 LR(0)分析算法1.flv 5.3.2 LR(0)分析算法2.flv 5.3.3 LR(0)分析算法3.flv 5.3.4 LR(0)分析算法4.flv 5.4 SLR分析算法.flv 5.5 LR(1)分析算法.flv 5.6.1 LR(1)分析工具1.flv 5.6.2 LR(1)分析工具2.flv 5.6.3 LR(1)分析工具3.flv 6.1.1 语法制导翻译1.flv 6.1.2 语法制导翻译2.flv 6.1.3 语法制导翻译3.flv 6.2.1 语法制导翻译的实现原理1.flv 6.2.2 语法制导翻译的实现原理2.flv 6.3.1 抽象语法树1.flv 6.3.2 抽象语法树2.flv 6.3.3 抽象语法树3.flv 6.3.4 抽象语法树4.flv 6.4.1 抽象语法树的自动生成1.flv 6.4.2 抽象语法树的自动生成2.flv 7.1.1 语义分析的任务1.flv 7.1.2 语义分析的任务2.flv 7.1.3 语义分析的任务3.flv 7.2.1 语义规则及实现1.flv 7.2.2 语义规则及实现2.flv 7.2.3 语义规则及实现3.flv 7.2.4 语义规则及实现4.flv 7.3.1 符号表1.flv 7.3.2 符号表2.flv 7.3.3 符号表3.flv 7.4 语义分析中的其它问题.flv
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值