编译原理 词法分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hit_shaoqi/article/details/82976833

词法分析负责将字符流转为符号流(单词流)

注意:空格已经被删掉,因为没有具体的意义,在文末追加eof。

词法分析的实现方案: 1. 手工编码 GCC LLVM 2. 词法分析器的生成器 自动生成 

手工构造:

代码实现:

通过状态转移图进行识别,如果识别到了其他字符,如>a,识别到了a,那么需要回滚,即*处。

自动生成的方式:

输入声明式的规范,经过工具如c(lex flex),可以自动产生一个词法分析器。

1)如何获得声明式的规范? 正则表达式

 如何使用正则表达式表示程序语言设计中的词法?

语法糖 syntax sugar 

语法糖简化了编写。

声明式的规范即是re 正则表达式

2)如何实现该工具?

3)词法分析器

有限状态自动机

状态转移函数(q0,a)->q1,即当在q0状态遇到a时,转移到q1状态

从q0经过a,可以走到q0,q1即下一状态是一个状态集合(NFA 非确定)。

如果在所有状态上对于单一字符,下一个状态是确定的,则是DFA(确定的)。

NFA对于一个字符是否能走到可接受状态并不确定,因此可能需要回溯。

注意,NFA存在空串,即表示从一个状态转移到另一个状态可以通过空串转移,即直接跳转。

DFA的实现:有向图 1. 边上的信息 (字符)2.节点信息(如输入节点,输出节点)

如何从RE转换到NFA?

1. Thompson 算法 RE-> NFA

如下图所示即为该表达式对应的正则表达式

NFA->DFA

即将一个节点经过一个字符后能够达到的状态集合归纳为一个集合

需要注意的是,接受状态n9包括在了多个集合中,q1,q2都是接受状态。

1. 先对任何一个子集求转换过程,求结果

2.对结果求delta

基于深度优先去做

基于宽度优先去做

NFA转换为DFA

D[q,c]<- t 即为将子集之间字符读取,状态转换关系进行记录

worklist就是每次得到的状态的集合,实际上类似于深度优先节点中的父节点连接其子节点

q1,q2,q3中都有n9,除q0外都是接受状态。

子集构造算法:

1. esp_closure 给定一个集合,确定集合中起始节点经过eps转换能够达到的状态;

2. Q DFA里面所有的有限状态自动机

3. worklist 队列

4. 进入worklist的循环

    对于每一个字符,asic码c, 

     delta(q,c) 经过字符c可以到达的状态,改状态经过e-close,得到t

    则将q经过可以到达t的关系记录到D中(DFA)

最终所有的NFA中的状态都被考虑过了。

DFA的最小化:

Hopcropft对DFA进行最小化

q2,q3都包括接受状态,可以合并,q1和q4也可以合并

n非接受状态,a接受状态

对于所有的char b c A的集合都不会被分割,因此将q1,q2,q3合并为集合A

实际上是将经过输入状态的字符后,仍然跳转到相同状态的状态合并为一个集合

DFA的代码表示:

DFA是一个有向图,图,就可以用表来表示。

转移表 有n*m,n为状态集合个数,m为字符个数

nextToken 每次返回一个识别的字符

在while循环中,如果是接受状态,则清空,通过table【state】【c】进行查表

当读取到结束字符后面的字符时,将栈弹出,rollback 回退,回退回去

stack用于实现最长匹配 

栈底每次都是接受状态,每次接收到接受状态后,清空栈,将接受状态作为栈底。

当接受失败后,回滚,直到回滚到接收到状态弹出栈,即最后一个匹配的,最长匹配。

跳转表每个状态对应一长段代码

当使用代码失陷后,跳转表比转移表局部性更好,转移表可能代码量很大(状态很多),而跳转表每次只执行一段。

猜你喜欢

转载自blog.csdn.net/hit_shaoqi/article/details/82976833