一段时间前,我们用go编写了python的词法解析器。由于近一段时间事情繁多,同时囊中羞涩,因此更多的精力投入到了和“变现”相关的工作,对编译原理,数据库这些极为基础且底层的技术有所忽略,毕竟他们不像reactjs, javascript,后台开发等这些工程性技术那样容易挣钱,因此属于用爱发电的范畴。但是工程性工作做多了我也发现一个问题,那就像人吃多精细食品而没有适当摄入粗粮,这会使得人有气无力,气虚多汗,让人感觉到体内虚空,没有底层理论和技术支持,一切上层构建都搭承在脆弱的地基上,随时有坍塌的危险。

为此我再次回归到底层技术,这次希望能沉淀下来,把编译原理,数据库系统等底层技术的设计和算法思想说清楚,这些都是当前繁花乱眼的上层技术的根基,掌握好他们,我们就能在风云变幻的信息技术世界站稳了根基,无论各种流行的技术如何变迁,我们都能以万变不离其宗的姿态淡定面对。

我们上一次完成python语言的词法分析时匆匆而过,忽略了一个很重要的数据结构和算法概念,其实词法解析并不仅仅是对字符串的简单处理,它基于一个根本概念叫有限状态自动机,大家如果在云课堂上看过我的“自己动手用java写编译器”课程就会发现,那里我用了大量的篇幅和代码来说明这个东西。为了改变上次浮皮潦草的态度,这次我打算认认真真基于编译原理“圣经”,也就是在龙书的基础上,将编译原理的算法和理论慢慢展现出来,同时将它们以Python编译器的形式逐步实现,这样我们才能够“知行合一”,不仅仅将认知停留在似懂非懂的理论上,同时也能破除编译原理给人晦涩难懂的感觉。

进入正题。我们先了解编译器的基本结构。所谓编译实际上就是将一种语言所表述的内容用另一种语言说出来。因此编译器的基本功能在于“分析”与“合成”。”分析“是把源语言分解成多个基本单元的组成,然后检测这些基本单元的结合形态是否满足特定语法结构。如果满足那么在此基础上将其转换成一种中间形态,也叫中间代码,例如java编译器将java代码编译成的字节码就属于这个东西,这个步骤也叫编译前端。如果这个过程发现源代码的基本单元在组合过程中出现语法或句法错误,那么编译器就会报告出来。分析过程主要是把源代码对应的基本单元组合情况进行了解,并把相应信息存储到一种叫做“符号表”的结构,然后将其与中间代码传递给合成过程。

合成主要任务是将分析过程传入的数据转换为目标语言。这个过程也叫编译器的“后端”。编译器在运行过程中会分为若干阶段:

在上图中,中间代码生成之前的部分叫前端,之后的部分叫后端。整个编译流程的第一部分叫词法解析或者是源码扫描,它把组成源代码的字符一一读入,然后检测这些字符是否能组合成满足条件的单元,如果可以则为这些单元用特定标记表示,标记也叫token,token的表现格式为<name, value>,然后它会传入后续步骤,也就是语法解析。所谓的value其实是一个数值,用来代表字符串所属的集合,例如类似于"1234",“1.23”,"1e23"这类字符串统一归属为NUMBER范围,因此可以给他们统一赋予一个数值1,“variable”, "my_str"这类字符串属于变量,因此统一赋予一个数值2表示,“for”, “if”, "else"等属于关键字,他们分别赋予不同的数值用来标明,value是当前所识别对象在符号表中的入口。

例如给定代码语句 position = initial + rate * 60,那么词法分析会做出如下输出:
“position”-><ID, 1>," ="-><ASSIGN, >, “initial”-><ID, 2>, “+”-><PLUS, > , “rate”-><ID, 3>,
“*” -> <MUL, >, “60”-><NUM, 4

看过上一节词法分析实现的同学会知道,这里的ID, PLUS等只不过就是一个常量数字,同时有限符号不会在符号表中有存储,例如操作符+,*这些。接下来语法分析会把上面的输出构造成一种二叉树结构,也叫语法解析树,二叉树的父节点都是操作符:

我们注意到数字60被转换成了<NUM,4>,其中60这个数值信息就好存在符号表入口为4的地方,后面我们会看到相关实现。由于语法解析树后,编译器需要查看其组成是否满足特定编程语言的语法,这个过程也叫语义分析,同时还要收集各个变量的类型信息,这个过程还要进行类型检测,例如PLUS操作不能跟着一个NUM节点和STR(字符串常量)节点.这个阶段还会进行类型转换,例如PLUS跟着一个整形变量节点和浮点型变量节点,那么它有可能会隐性将整形节点转换为浮点型(上图中的inttofloat),对应golang编译器而言,这种转换就不会发生,同时报告错误,除法代码自己进行了显示转换。

完成了语义分析,编译器会根据语法树生成中间代码。一种常用的中间代码叫”三地址码“,也就是每条语句最多包含三个操作数,每个操作数都能放在寄存器中,上面语法树就可以构建如下中间代码:

t1 = inttofloat(60)
t2 = id3 * t1
t3 = id2 + t2
id1 = t3

三地址码有个特性,操作符会位于等号的右边,其中t1,t2,t3都是临时变量,用于存储中间过程的计算结果,三地址码语句中最多有三个变量,同时允许变量少于3个。接下来阶段叫代码优化,其实就是尽可能减少三地址码的数量,例如上面指令中最后一条其实没有必要,同时可以直接用浮点的60.0替换掉整形60,于是中间代码优化器就会把它去除形成如下结果:

t1 = id3 * t1
id1 = id2 + t1

代码优化是个复杂过程,因为面对不同cpu或指令集会有不同的优化方式。在编译过程中,有很大部分的时间就消耗在优化阶段。最后是代码生成,编译器会将中间代码转换为对应平台的指令集,于是中间代码的操作会转换为目标语言的指令,变量会转换为寄存器或内存地址,例如上面代码可以转换为如下指令集:

LDF R2, id3
MULF R2, R2, #60.0
LDF R1, id2
ADDF R1, R1, R2
STF id1, R1

LDF, MULF等式操作指令,跟着的第一个单元为操作结果存放地址,其中的F表示进行浮点操作。LDF表示加载操作,例如LDF R2, id3表示将id3的内容存放到寄存器R2,然后将R2里面的内容与浮点数60.0进行乘法操作并将结果存放到寄存器R2,后面指令以此类推。

这些过程其实还有很多工程性问题需要解决。例如变量的地址分配,这个时候就需要符号表的帮助,因为符号表记录了变量的类型,于是编译器知道所需地址有多大,对于函数对象,符号表还会记录输入参数的数量,类型等,同时还能知道如何传递参数以及函数的返回值类型等,符号表是一个需要深入了解的数据结构,后面我们会详细分析。

所有这些内容都来自于编译原理的经典书:龙书。如果你看过我对"自己动手用java写编译器“,那么就能比较容易理解其内容,要不然你读起来会云里雾里,不知所云。我们后面会将龙书中的算法进行实践,特别是用来做一个”简易“版python编译器,只有通过动手实践,我们才有可能掌握复杂的编译原理算法。

尝试再造python编译器:龙书重制版相关推荐

  1. 龙书啃不动?老司机带你从零入门编译原理,开发编译器

    计算机只认识二进制的,但是我们平常开发中根本不会使用二进制进行开发,我们使用的都是 Java.C.Python 这类的高级语言.每种语言都会经过一系列的转换才能被计算机识别,那么到底是谁做的这项工作呢 ...

  2. 1338_龙书笔记_001_编译器的大概结构以及工作的基本流程

    全部学习汇总: GreyZhang/g_compilers: learn some basic compiler Principles, Techniques, and Tools (github.c ...

  3. 手撕龙书 第一章 认识编译器

    编译原理 编译的原理 什么叫编译? 编排和翻译. 编排什么? 编排人理解的语言. 翻译成什么? 翻译成机器能理解的语言. 根据以上, 所以 编译=高级编程语言–>中间代码–>最终的机器语言 ...

  4. 龙书(附录A):一个完整的编译器前端(学习记录)

    (龙书)完整的编译器前端下载地址:ps:我设置的不要下载积分,如果还是不能下载的话请评论区留言. https://download.csdn.net/download/Zheng_lan/167792 ...

  5. 龙书《编译原理》摘要 第一章 引论

    文章目录 0. 前言 1. 语言处理器 2. 编译器基本结构 3. 一些常用术语 0. 前言 最近开始研究TVM源码,总感觉自己抓不住主线. 说是实现了新语言Relay,里面那些奇怪的东西是什么?是类 ...

  6. 《最终幻想7 重制版》DEMO体验:讨论ATB战斗系统的一些问题

    仅基于DEMO,正式版让我们共同期待 刚刚通关,我个人对这个DEMO的评价还是比较高的,整个玩下来流程不长,大概半小时到四十分钟,但是刚刚好,节选选的相当合适,在一个小情节里把该展示的都给玩家展示了, ...

  7. python 编译器_Python教程:编译器与解释器

    一.数据的表示方式 我们都知道,现实生活中,数字的表示方式有很多种,常见的有二进制.八进制.十进制和十六进制.十进制我们都很熟悉,加法口诀表我们都背过,主要是使用0~9,这10个阿拉伯数字来构建整个十 ...

  8. 编译原理(龙书)学习笔记 第一章

    编译原理(龙书)学习笔记 第一章 1.1语言处理器 解释器(interpreter) : 编译器(compiler): 一个语言处理系统 练习 1.1.1:编译器和解释器之间的区别 1.1.2:相对优 ...

  9. 龙书虎书鲸书啃不动?试试豆瓣评分9.5的猴书

    相传,编译原理界有三大圣书: 龙书是为Compilers: Principles, Techniques, and Tools: 虎书是为Modern Compiler Implementation ...

最新文章

  1. Android开发环境搭建与起步--太详细了,一步一步图文教你
  2. Struts2之Ognl
  3. 如何成为python 数据分析师_如何七周成为数据分析师20:了解和掌握Python的函数...
  4. Spring运行期间配置文件解析返回
  5. spring源码分析的书到了
  6. java character是什么意思_Java Character 类
  7. android activity透明主题,Android应用的全透明效果--Activity及Dialog的全透明
  8. header python 环境信息_python获取网页header头部信息(python小白学习笔记二)
  9. 好男人是这样爱老婆的
  10. 写给我们奔三的80后们……
  11. office2016鼠标右键没有新建word等
  12. HTML标签与CSS样式
  13. 【PCA、LDA降维,及模型评估(SE,SP,AUC)】
  14. 2022国赛数学建模思路 - 案例:线性优化-粒子群算法
  15. 目标检测(十一)——DSSD
  16. AVPlayer与AVPlayerViewController媒体播放器
  17. mouseover和mouseenter的异同
  18. html页面,文字的自动换行
  19. 飞龙:蒙语“牵手”人工智能的拓荒者
  20. 华为鲲鹏专家解读:90%代码如何移植到鲲鹏平台

热门文章

  1. php 读取文件大小限制,PHP fread():读取文件(任意长度)
  2. 软件工程之面向对象的设计原则
  3. Mr.Alright---如何通过omnipeek抓取sniffer log
  4. 信息技术课件认识计算机ppt课件ppt,小学信息技术课件-认识计算机课件PPT课件.ppt...
  5. AVB源码学习(二):Uboot阶段AVB2.0校验流程
  6. 什么是B2B电子商务模式
  7. MJKDZ PS2手柄控制OskarBot小车(三):STM32接收无线串口模块的数据并处理
  8. nginx开启https配置
  9. 影楼管理系统需要用服务器吗,影楼发展不起来,不是管理者没有能力,而是这些原因...
  10. CSS进阶(一)背景与边框