【编译原理】第二章,词法分析(更新)

简介: 【编译原理】第二章,词法分析

文章目录

词法分析

这一章,简而言之就是如何实现词法分析器,完成以下的流程

如何实现?词法单元如下,我们要将字符流提取出词素


以下面这个举例:我们要从下面的字符流中提取出 常量,变量,预保留字,如何扫描一次就能区分?

return ( initial <= 10 ) ? 100 : ( position + initial ** 2 ) ;

为了能够更容易的区分,变量的定义要以下划线或字符开始,只能由字符,数字,下划线组成。这就是为什么我们在写程序的时候,变量要这么定义,目的是为了编译!

下面是解析的举例过程


当forward指向( 时,什么都不符合了。从Ps 至 (forword - 1)的串, 为一个保留字。保留字优先变量。


我们可以得到一个结论,常量和变量很容易提取出来,保留字的提取就要根据特定的正则表达式了。return符合特定的保留字正则表达式,所以被认为保留字提取了出来。我想之后的也能被提取。


首先明白语法分析的三个概念


词法单元(和类相似)

自定义符号:变量(数据和函数),常量(数值和字符串)

预定义符号:if,for,while,++,+

词素(和实例相似)

词法单元的实例,比如说,123是数值的实例,abc是数据变量的实例

预定义符的实例只有一个

词法单元的模式(类的构成法则)

第一个问题来了,如何判断哪个词素属于哪个词法单元?这是非常重要的问题,123321我们判断为数值,还是变量,还是预定义符?


我们要根据词法单元的模式进行区分,也就是词法单元本身的构成规则来找到匹配的模式,我们用正则表达式来描述这种匹配规则,来描述字符串的集合


正则表达式

正则语言是一门最简单的语言,用它描述目标语言的词法单元及其构成法则,其本质是描述字符串的集合


正则表达式运算方式有:


并运算 例如:L∪M = L | M= {s| s∪L 或者s∪M}

连接运算 例如:{st |s∈L ,t∈M}

闭包运算 例如:L*=∪i=0∞Li

下面是要记住的正则表达式例子,将用来进行词法分析


letter_ → [A- Z a - z _ ] 字母和下划线

digit → [0 - 9] 数字0到9

id → letter_ (letter_ | digit)* 自定义变量

digits → digit+ 0到正无穷

number → digits (.digits )?( E [± ]?digits)? 常量

C语言的语法工构成法则为


lexicalRule → reservedWord | id | numberConst|stringConst | note | blankSpace | crlfLexeme


语法构成为:预定义符,变量,数字常量,字符常量,注释,空格,回车


正则表达式的状态图

将字符流切分成词素流,表达了一个词素的获取过程


正则表达式: id → letter_ (letter_ | digit)*


正则表达式: if → if


我们知道了C语言的词法分析可以由一个正则表达式来表示,且正则表达式有唯一的状态图,那我们就可以用一个状态图来描述C语言的词法分析

如果用代码来表示状态图之间的变换?

状态图的数据结构是:

class State(
    currentState INT,
    driverChar CHAR,nextState INT,
    type CHAR(CHECK VALUE IN('0','M','E','I');
    category VARCHAR IN ('reserved','id','num','string');
);

0’: 起始状态,‘M’:中间状态,‘E’:匹配状态(不包含exclude),‘I’:匹配状态(包含include)

基于状态转换图的词法分析框架,所有的语言都是类似的


现在问题转化成了如何得出正则表达式的状态转换图?


有穷计算机

将状态转换图形式化,上升为一种理论知识,被称作有穷自动机,分为下面两类


非确定有穷自动机NFA(Nondeterministic Finite Automata)

特点:一个驱动符号(即边上的符号)可以引出多条边,ε可以是边上的符号


下面是 (a|b)*abb正则表达式的NFA


确定性有穷自动机DFA(Deterministic Finite Automata)

特点:有且仅有一条出边。驱动符号不包含ε

下面是 (a|b)*abb正则表达式的DFA



由正则表达式到NFA,再由NFA到DFA,再由DFA到状态图转换程序,这就是上面的答案,并且我们每一步都要完成


正则表示式到NFA

对于单个字符的表达式r = a,构建其NFA


对于表达式r =s | t ,构建其NFA


对于表达式r =s t ,构建其NFA


对于表达式r =s + ,构建其NFA


对于表达式r =s* = ε| s+ ,构建其NFA


对于表达式:r =s?=s | ε


NFA到DFA

对正则表达式的NFA,并不能用于词法分析器的构造。因为NFA中存在不确定


使用穷举办法,把所有可能情况列举出来,把不确定性转变成确定性


例如:




一系列转化过程后,得到转换表,以及最后的结果



值得注意的是,这个得到DFA并不是最简的状态,还能继续简化,可以使用相同的操作,穷举



在构建运算符的NFA时,消去或者减少ε边,就会得到最简的DFA


从正则表达式到DFA,看似不难,却又无从下手。一个由空字符ε驱动的状态迁移,从表面上来看,显得多此一举,毫无意义。但它把问题梳理得直观易懂了,很有说服力了,条理很清晰了。它使得正则表达式到NFA有个一一对应关系,NFA到DFA也有个一一对应关系。这就是数学思维的魅力,艺术的魅力。


DFA到状态图转换程序

留给我们之后具体实现


相关文章
|
7月前
|
自然语言处理 前端开发 编译器
编译原理 - 语法制导翻译
编译原理 - 语法制导翻译
69 0
|
自然语言处理 C语言
编译原理实验-词法分析
编译原理实验C语言实现
115 0
|
3月前
|
存储 C语言
C语言程序设计核心详解 第七章 函数和预编译命令
本章介绍C语言中的函数定义与使用,以及预编译命令。主要内容包括函数的定义格式、调用方式和示例分析。C程序结构分为`main()`单框架或多子函数框架。函数不能嵌套定义但可互相调用。变量具有类型、作用范围和存储类别三种属性,其中作用范围分为局部和全局。预编译命令包括文件包含和宏定义,宏定义分为无参和带参两种形式。此外,还介绍了变量的存储类别及其特点。通过实例详细解析了函数调用过程及宏定义的应用。
|
6月前
|
自然语言处理 编译器 测试技术
详尽分享词法分析程序
详尽分享词法分析程序
30 2
|
自然语言处理
【编译原理】第二章,词法分析
【编译原理】第二章,词法分析
|
自然语言处理 IDE 开发工具
【编译原理】第三章语法分析
【编译原理】第三章语法分析
|
自然语言处理 Java 编译器
【编译原理】第一章,什么是编译原理?
【编译原理】第一章,什么是编译原理?
|
自然语言处理 算法
第二章 总结及作业(6789B)【编译原理】
第二章 总结及作业(6789B)【编译原理】
166 0
|
存储 自然语言处理 Unix
编译原理 实验一:词法分析器的自动实现(Lex词法分析)
编译原理 实验一:词法分析器的自动实现(Lex词法分析)
1079 0
编译原理 实验一:词法分析器的自动实现(Lex词法分析)
|
自然语言处理 编译器
编译原理之词法分析器随笔和简单实现
编译原理之词法分析器随笔和简单实现
编译原理之词法分析器随笔和简单实现