所属章节
第2章 程序设计语言基础知识 -> 2.2 语言处理程序基础 -> 2.2.2 编译程序基本原理
自动机\状态机的分类
两个维度:是否确定,是否有限
视频总结:https://www.bilibili.com/video/BV1S7411C7Jh/?spm_id_from=autoNext
https://www.bilibili.com/video/BV1Ma4y1t79D?spm_id_from=333.999.0.0
非确定有限状态机NFA
Nondeterministic Finite Automaton
特点:
存在某状态,对于同一个输入字符,有不同的输出状态
即给定一个状态和一个输入,我们无法确定地转换到下一个状态
判定依据(满足以下任意一个条件):
1、状态(节点)的不同输出边,标有相同的字符;
2、输出边包含空字符ξ
ξ闭包的计算
ξ边表示空字符边。代表输入空。若有正则a*b*
,* 代表a\b可以不出现,或者出现一到多次。
状态集SS={S1, S2,…, Si,…,Sn},
状态Si的ξ闭包close_ξ({Si}),
状态Si,Si+1 的ξ闭包close_ξ({Si, Si+1}) = close_ξ({Si}) ∩ close_ξ({Si+1}),
计算
1、状态Si的ξ闭包肯定包含Si自己
2、从状态Si 出发,连续输入n个ξ,可以到达的所有状态
确定有限状态机DFA
Deterministic Finite Automaton
特点
给出一个输入,总能转移到确定的下一个状态
NFA转换为DFA
转换过程:
1、获取NFA的所有字符,和输入状态的ξ闭包(后面称为S0)
。初始化表。
2、补充表信息。
若存在空字符ξ边时,则计算某个字符的输出状态时,必须考虑ξ闭包做交集。
根据NFA,获取S0
的“字符闭包和ξ闭包”的交集
,得到所有字符闭包交集的集合。
得到的每个交集
,都将作为DFA的一个状态
。
3、将得到的DFA状态作为出发状态,计算该状态的“字符闭包和ξ闭包”的交集,重复步骤2。
4、得到所有DFA状态,和他们的字符闭包。关系如表所示。根据表信息,得到DFA。
DFA 不会包含空字符边(ξ边)
举例
带ξ边
不带ξ边
1、初始化表:
获取 DFA 初始状态:
DFA的初始状态 == NFA初始状态的ξ闭包
因为NFA的初始状态为0,且不包含空字符ξ边,故 close_ξ({0}) = {0}
2、计算每个DFA状态的字符闭包
4、根据表,画出 DFA。
完整的NFA\表\DFA图如下所示。
上下文无关文法
中间代码
中间代码不是正规式
- 中间代码的几种表现形式:
- 1、表达式:前缀表达式、中缀表达式、后缀表达式(又称逆波兰式)。
分类依据:根据二元运算的运算符放在最前、中间、最后。 - 2、树形表示:二叉树形式,运算符作为根节点。
- 。。。
- 1、表达式:前缀表达式、中缀表达式、后缀表达式(又称逆波兰式)。
类似于将运算符作为二叉树的根,进行二叉树的前序遍历、中序遍历、后序遍历。
转换方法:
方法一:二叉树遍历得到的字符序列。求后缀式则利用后序遍历,求前缀式则利用前序遍历,求中缀式则利用中序遍历。
方法二:给每个二元运算式加括号,求后缀式则将其中的运算符移到右括号外侧;求前缀式则将其中的运算符移到左括号外侧。
举例:算术表达式->后缀式
表达式 (a-b)(c+5) 的后缀式为:ab-c5+
解法一:二叉树的后序遍历
表达式转化为 二叉树,对二叉树进行后序遍历得到 后缀式
解法二:给每个二元运算式加括号,求后缀式则将其中的运算符移到右括号外侧
—加括号–> ((a-b)*(c+5))
—移动运算符–> ((ab)-(c5)+)*
—去括号–> ab-c5+*