课程里涉及到的内容讲的还是很清楚的,但个别地方有点脱节,任何看不懂卡住的地方,请自行查阅经典著作《Compilers——priciples, Techniques and Tools》(也就是大名鼎鼎的龙书)的对应章节。

一. 编译的基本流程

完整的编译的5个基本步骤包括lexcical anlysis,parse,sematic,optimize,code generate。课程中并没有使用复杂的编程语言,而是一种用于课堂教学的自发明语言COOL,很明显老师为它写好了编译器程序。

二. Lexical Analysis(词法分析阶段)

任务:将字符串分解成为[Type, (Value)]元组的形式的词法单元。

“龙书”里的示例更为直观,例如表达式语句 E = M * C ** 2进行词法分析后会得到如下的类似结果:

[id,指向符号表中E的条目的指针]

[assign_op]

[id,指向符号表中M的条目的指针]

[mult_op]

[id,指向符号表中C的条目的指针]

[exp_op]

[number,整数值2]

词法分析基本需要经历如下几个阶段:

Lexical Specification——>Regular expressions——>NFA——>DFA——>Table-driven Implementation of DFA

2.1 Lexical Specification(分词原则)

COOL中的基本Type包括如下几个类别:

Indentifier标识符-指以字母开头后续为若干个字母或数字的字符组

Integer-指一组非空的数字字符

Keyword- 指语言中的关键词,例如if,else等

Whitespace- 指一组非空的空格字符或换行符或制表符

很多程序设计语言中的分词原则基本都会覆盖关键字,运算符,标识符,常量,标点符号,他们也会在后面的实现中被作为终止符集合,课程板书中也提供了COOL分词原则的类正则形式。

分词时类型的正则匹配默认为贪婪模式,即匹配更多的字符。词法单元也具备一定的优先级次序(通常也是代码逻辑的实现顺序),例如if从正则上来判断既符合Keywords也符合Identifier,此时该单元的类型就应该标记为Keywords。这个阶段就完成了从Lecical Specification——>Regular expressions的部分。

2.2 Finite Automata (典型分词算法-有穷自动机)

FA是一个可以自动识别词法单元的机器,它是一个状态转换图,“有限”是指它包含的状态是有限的,一个状态读入一个字符后,后继的状态可能为:

后继状态为自身

后继状态只有一个

后继状态有多个

如果每次转换后的后继状态都是唯一的,则称为DFA(确定有限自动机),如果后继状态可能有多个则称为NFA(不确定有限状态机)。由于DFA的状态转移路径是唯一的,所以作为状态查询图时,无论成功或者失败只需要运行一次,但NFA就可能需要运行多次。

正则表达式是可以转换为NFA形式的,或许你已经在一些可视化正则表达式的网站上[https://regexper.com ]见过类似的形式。下图比较清晰地展示了从正则表达式到NFA状态图的转换规则(Regular expressions——>NFA):

如果一个DFA和一个NFA能够识别的字符集是一致的,则称它们为等价的,对于任意NFA,一定存在一个DFA与其等价,由NFA构建DFA的过程被称为DFA的确定化,也就是NFA——>DFA的过程。这个过程是围绕ε -closure状态集合的概念展开的,大致的过程就是从起点开始,每次将当前状态和通过若干次ε转换(它是一个特殊的状态转移函数,表示转换后的状态还是当前状态)作为一个新的ε -closure状态集合 ,使用矩阵记录每个ε -closure集合转换前后的集合,最后对整个状态转移矩阵进行标记重命名,就可以得到一个DFA,事实上转化后的DFA中的每一个状态,就是NFA中的一个ε -closure集合,你可以将它理解成一个通过分组来简化表达方式的过程,相关的过程可以参考下面这个文章西北农林科技大学编译原理课程PPT【词法分析】,里面图比较多,能够辅助理解,本文不再赘述。

三. 手动实现分词器

至此1-4课就结束了,估计看视频课程的人也是一脸懵逼,因为课程并没有讲解如何利用DFA得到最终期望的形式——Token元组,那么最后我们就自己手动来实现一下。

3.1 基本定义

假设我们需要对下面这段代码进行分词解析:

let snippet = `

var b3 = 2;

a = 1 + ( b3 + 4);

return a;

`;

那么先来进行一些基本类型集合定义:

//解析结束标记

const EOF = undefined;

//Token Type 可识别的Token类型,

const TT = {

num: 'num',

id: 'id',

keywords: 'keywords', //var | return

lparen: 'lparen',// (

rparen: 'rparen',// )

semicolon: 'semicolon', //;

whitespace: 'whitespace', // \n | \t | \s (空格,制表符,换行符)

plus: 'plus', // +

assign: 'assign',// =

}

// 状态集类型,除开始和结束外,其他可以与Token支持的类型相对应,每次分词从start状态开始,接收一个字符后改变状态,直到在done状态结束时,可以得到一个token

const S = {

start: 'start',

done: 'done',

...TT

}

进行工具函数定义:

//判断是否为关键词(为简化流程,仅检测上面示例中包含的关键词)

const isKeywords = (token) => ['function', 'return', 'if', 'var'].includes(token);

//判断是否为数字

const isDigit = c => /\d/.test(c);

//判断是否为合法的标识符字符

const isValidId = c => /[A-Za-z0-9]/.test(c);

//判断是否为空格

const isBlank = c => /(\s|\t|\n)/.test(c);

3.2 构建DFA

以上面定义的状态集合和token类别为依据构建DFA:

3.3 开始分词

分词的逻辑实际上就是,每次先将状态置为start,然后读入一个字符,根据该字符判断下一个状态,只要没有到达完成状态done就继续读入字符,每次到达done状态时,就可以得到一个token,将其记录下来,然后重新将状态置为start,开始寻找下一个token直到分析完整个代码段。也就是说DFA状态机每运行一轮,就得到一个token。参考代码如下:

/**

* 词法分析

*/

function tokenize(code) {

let state = S.start;

let currentToken;//标记当前寻找到的token

let index = 0;//起始指针,每次分析指向start状态

let lookup = 0;//前探指针,每次分析最终指向done状态,start->done之间的字符即为token

while (code[lookup] !== EOF) { //如果还有字符

while (state !== S.done) { //开始拆分token

//获取下一个字符

let c = code[lookup++];

//根据当前状态和下一个字符判断DFA如何跳转

switch (state) {

case S.start: //开始为空集,实现DFA中各个状态转移分支

if (isDigit(c)) {

state = S.num;

} else if (isValidId(c)) {

state = S.id;

} else if (isBlank(c)) {

state = S.done;

} else if (c === '=') {

currentToken = [TT.assign, '=']

state = S.done;

} else if (c === '+') {

currentToken = [TT.plus, '+']

state = S.done;

} else if (c === ';') {

currentToken = [TT.semicolon, ';']

state = S.done;

};

break;

case S.num: //如果是整数

if (isDigit(c)) {

state = S.num;

} else {

currentToken = [TT.num, code.slice(index,lookup - 1)];

lookup -= 1; //从数字状态跳出后,最后一位需要参与下一轮分词,故回退一位

state = S.done;

}

break;

case S.id: //如果是标识符状态

if (isValidId(c)) {

state = S.id;

} else {

let tempToken = code.slice(index,lookup - 1);

lookup -= 1; //从标识符状态跳出后,最后一位需要参与下一轮分词,故回退一位

if (isKeywords(tempToken)) {

currentToken = [TT.keywords, tempToken];

}else{

currentToken = [TT.id, tempToken];

}

state = S.done;

}

break;

}

}

//state = S.done时跳出

currentToken && console.log(currentToken);

currentToken = undefined;

//起指针跟上末指针

index = lookup;

//开始下一轮分词

state = S.start;

}

}

3.4 查看分词结果

运行上述代码即可看到目标程序片段的分词结果:

四. 小结

至此,我们就得到了元组形式的分词结果,完成了编译中第一步lexical analysis的部分,笔者同时提供了一份包含token所在行列信息的版本,你可以从附件或【我的github仓库】中拿到示例代码,如果觉得对你有帮助,可以在github上为我加个星星哦~

关于找一找教程网

本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。

本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。

[Stanford公开课《编译原理》学习笔记(1~4课)]http://www.zyiz.net/tech/detail-91416.html

HTML4基本编译原理,Stanford公开课《编译原理》学习笔记(1~4课)相关推荐

  1. Open SAP 上 SAP Fiori Elements 公开课第一单元学习笔记

    Open SAP 课程地址 这门公开课的教学大纲: 第一单元:Painting the big picture 本课程将使用 SAP Fiori Elements 开发一系列的应用,如下图所示: Th ...

  2. SAP Fiori Elements 公开课第二单元学习笔记:Fiori Elements 架构

    课程地址 第二单元内容:讲解 Fiori Elements Architecture 使用 SAP Fiori Elements,SAP 负责提供应用 floorplans,而应用开发人员只负责专注于 ...

  3. 吴恩达机器学习公开课第一周学习笔记

    Octave是一种编程语言,旨在解决线性和非线性的数值计算问题.Octave为GNU项目下的开源软件,早期版本为命令行交互方式,4.0.0版本发布基于QT编写的GUI交互界面.Octave语法与Mat ...

  4. Stanford公开课《编译原理》学习笔记(2)递归下降法

    [摘要] javascript实现递归下降语法解析 示例代码托管在:http://www.github.com/dashnowords/blogs B站地址:[编译原理] Stanford公开课:[S ...

  5. Stanford公开课《编译原理》学习笔记(1~4课)

    [摘要] 编译原理基础 示例代码托管在:http://www.github.com/dashnowords/blogs B站地址:[编译原理] Stanford公开课:[Stanford大学公开课官网 ...

  6. 公开课计算机原理,《计算机的基本工作原理》公开课材料

    <<计算机的基本工作原理>公开课材料>由会员分享,可在线阅读,更多相关<<计算机的基本工作原理>公开课材料(11页珍藏版)>请在人人文库网上搜索. 1. ...

  7. 教师教学公开课创意黑板学习PPT模板

    模板介绍 精美PPT模板设计,教师教学公开课创意黑板学习PPT模板.一套说课PPT幻灯片模板,内含灰色多种配色,精美风格设计,动态播放效果,精美实用. 一份设计精美的PPT模板,可以让你在汇报演讲时脱 ...

  8. [MIT公开课(计算机教育中缺失的一课)]2.Shell工具与脚本

    (该系列文章大部分内容来源于MIT课程笔记,加入了个人的理解.原笔记中没有的细节和其他的需要理解的内容,公开课地址:https://www.bilibili.com/video/BV14E411J7n ...

  9. 【原】机器学习公开课 目录(课程笔记、测验习题答案、编程作业源码)...持续更新......

    之前看过的机器学习课程.本文是相关课程笔记.习题答案.作业源码的电梯. 1 Coursera 斯坦福机器学习课程,Andrew Ng 1.1 说明 课程地址和软件下载 Coursera连接不上(视频无 ...

最新文章

  1. jdk8新特性 lambda表达式详解
  2. python基础知识资料-学习Python列表的基础知识汇总
  3. SAPGUI系统登录页面配置的SAProuter有什么用
  4. 社会大学的作业,活动篇
  5. 跟我学XSL(二) -XSL的运算符
  6. c++自底向上算符优先分析_PHP程序员从入门到佛系第十弹:PHP 运算符
  7. JS实现App扫码网页端登录
  8. 问题 I: 成对最小公倍数(数学)
  9. 【转】02.Dicom 学习笔记-DICOM C-Find 消息服务
  10. linux 两块硬盘 分区,linux 两块硬盘分区,这个是什么/dev/mapper/VolGroup00-LogVol00
  11. 计算机没有设置无线临时网络,如何创建临时无线网络
  12. MQTT服务器搭建和测试步骤及遇见的问题
  13. 编程之美 3.1 字符串移位包含问 复杂度(O(N*K)
  14. [Giveaway] 来自AnyBizSoft、4Media、EASEUS的限时免费软件
  15. vector容器——构造函数
  16. 《计算机组成原理》作业,《计算机组成原理》作业一解答.doc
  17. 第一台电子计算机乌拉尔,2018网易爱乐之战题目答案大全一览
  18. 僵尸网络瞄准Linux服务器
  19. 从零开始学习信号完整性--7-SI分析仿真
  20. 讯飞智能录音笔SR502:支持OCR识别功能的职场礼物

热门文章

  1. java 计算机开始时间,关于计算Java程序运行时间
  2. oracle表复制为mysql表_oracle 将数据库的表复制到另一个数据库表内
  3. js java 反射机制_java 类加载机制和反射机制
  4. 计算机user用户的密码存储在,windows存放用户密码的文件
  5. mysql还原txt表的字段结构,mysql 修改表结构(转)
  6. android getter不起作用,java - 如何在Android Studio中自动生成getter和setter
  7. 计算机控制用户自己编写什么软件吗,计算机控制软件技术基础.ppt
  8. java环境变量一闪而过_Java环境变量配置和Tomcat启动时cmd界面一闪而过问题
  9. koa mysql mongodb_koa 操作MongoDB数据库
  10. Linux升级glibc版本汉字乱码,Linux CentOS6升级glibc库过程