编译原理:词法分析

本文介绍了编译原理的关键步骤,包括词法分析器将源程序转换为记号,语法分析生成抽象语法树,以及使用正则表达式和自动机(如DFA和NFA)进行解析。还讨论了编译与解释的区别,以及自动纠错和数据结构在编译过程中的应用。
摘要由CSDN通过智能技术生成

开学了,我又来啦
教材:《编译原理与实践》

概述

词法分析->语法分析->语义分析->生成与优化
在这里插入图片描述

源程序->编译器->(静态分析)->目标语言->目标机器->(动态计算)->计算结果
精度无限时,计算结果理论上是一致的,实际上有差别。

在这里插入图片描述
编译与解释的区别
在这里插入图片描述
编译器流程,例:
a[index] = 4 + 2
1.扫描程序或词法分析程序(The Scanner)
输入:字符串序列“a[index] = 4 + 2”
输出:记号序列 a , [ , index , ],=, 4, + , 2
注意:每个记号有1个或多个字符组成;空格和换行符,自动跳过。
2.语法分析程序(The Parser)
输入:记号序列
输出:分析树->语法树(抽象语法树)
在这里插入图片描述
在这里插入图片描述
3.语义分析程序(The Semantic Analyzer )
输入:抽象语法树
输出:符合语义的抽象语法树
在这里插入图片描述
4.优化(The Optimizer )
在这里插入图片描述

5.中间代码生成(三元式)
在这里插入图片描述
7.目标代码生成与优化
在这里插入图片描述
编译器使用的数据结构
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

词法分析

词法分析器的主要作用是将输入的源程序(字符流)转换或切分成记号流(编译器内部定义的数据结构)。
实现方法:手动or通过生成器(申明式规范->Lex, fLex, jLex0>词法分析器)自动实现
流程:正则表达式-(汤姆逊算法/自底向上)>NFA-(子集构造,列表)>DFA-(子集合并分裂)>minDFA->词法分析器
扩展:

  • 能否实现自动纠错?
  • 输入缓冲太长怎么办?(双缓冲)
  • 如何判断是否到末尾?(加EOF)

记号(词法单元)

记号-示例-描述
const-const-const
if-if-if
relation-<,<=,=,<>,>=,>
id-pi,count,d2-字母打头的字母数字串
num-3.14,0,6.2e23-数字常数
literal-“core dumped”-""之间的字符
在这里插入图片描述
这是一个简单的实现(tiny c),c比之更复杂

数据结构

typedef enum{//枚举列出所有的记号类型
/* book-keeping tokens */
ENDFILE, ERROR,
/* reserved words */
IF, THEN, ELSE, END, REPEAT, UNTIL, READ, WRITE,
/* multicharacter tokens */
ID, NUM,
/* special symbols */
ASSIGN, EQ, LT, PLUS, MINUS, TIMES, OVER, LPAREN, RPAREN, SEMI
} TokenType;

Typedef struct {
TokenType tokenVal; /* 记号类型 */
char *stringVal; /* 记号字符串 */
int numVal;
} TokenRecord;

正则表达式

正则表达式-菜鸟教程
正则表达式r由它所匹配的字符串集合来定义,该集合为由正则表达式生成的语言,L(r)。

  • 语言组成:字符集与归纳规则

  • 字符集: 符号的非空有限集合 ∑ \sum

  • 归纳定义:
    空串是ε正则表达式,对应语言L(ε)={ε}
    如果字符a(a∈ ∑ \sum ),那么a是正则表达式,对应语言L(a)={a}
    如果r和s都是正则表达式,则以下也是正则表达式:
    选择:r|s 语言:L(r|s) = L®∪L(s)
    连接:rs 语言: L(rs) = L® L(s)
    Kleene闭包:r* 语言:L(r*) = L(ε)∪L ( r)∪L(rr) ∪ ……

  • 运算的优先级: 闭包 > 连接 > 选择

  • 正则表达式的等价性:如果两个正则表达式r和s表示同样的语言,则称r和s等价,记作:r = s

  • r|s = s|r
    r(s|t) = rs|rt
    r** = r* 幂等性
    选择有交换律、结合律
    连接有结合律,对选择可分配,但没有交换律
    闭包具有幂等性

  • 正则表达式的扩展
    在这里插入图片描述

  • 常用表达式:
    digit=(0|1|2|…|9)= [0-9]
    keyWords = if | while |for|…
    letter = A|B|C|……|Z| a|b|c|……|z|_ = [A-Za-Z_]
    id = letter(letter|digit)* 以字母或下划线开头的标识符
    digits = digit(digit)* = d i g i t + digit^+ digit+ 至少一位数字
    optionalFraction = .digits | ε 小数
    optionalExponent = E(+ | - | ε) digits | ε 浮点数
    number = digits optionalFraction optionalExponent=digits (. digits)? (E(+|-)? digits)?

例1: ∑ = { a , b , c } \sum =\{a,b,c\} ={a,b,c} ,则~a=b|c
例2:并不是所有字符串都可以用正则表达式表示,比如:由一个b及在其前后有相同数目的a组成的串S的集合(上下文相关)

有穷自动机

确定有穷自动机(DFA;Definite of Deterministic finite automation)
非确定有穷自动机(NFA;Non-deterministic finite automation)

有状态,用圆圈表示,状态之间可以用箭头表示转换,一个初态,多个终态。

DFA

  1. 用函数描述:
    M = ( ∑ \sum , S, T, s0, A)
    ∑ \sum 输入字母表(终极符集合)
    S 有穷状态集(非终极符集合)
    T :S× ∑ \sum →S 状态转换函数 是单值函数
    s0∈S 唯一的初始状态
    A ⊂ \subset S 终止状态集
    在这里插入图片描述

  2. 用状态表(状态转移矩阵)描述(考试建议)
    行表示状态,列表示输入字符,矩阵元素表示T(s,a)的值
    在这里插入图片描述
    例子:在这里插入图片描述
    代码实现:
    在这里插入图片描述

{ starting in state 1 }
if the next character is a letter then
	advance the input;
	{ now in state 2 }
	while the next character is a letter or a digit do
		advance the input; { stay in state 2 }
	end while;
	{ go to state 3 without advancing the input}
	accept;
else
	{ error or other cases }
end if
state := 1;
ch := next input character;
while not Accept[state] and not error(state) do
	newstate := T[state,ch];
	if Advance[state,ch] then
		ch := next input character;
		state := newstate;
end while;
if Accept[state] then accept;

NFA

M = ( ∑ \sum , S, T, s0, A)
∑ \sum 输入字母表(终极符集合)
S 有穷状态集(非终极符集合)
T :S×( ∑ \sum U{ε})->℘(S) (S的幂集) 状态转换函数 多值函数 包含空边
S0∈S 初始状态集
A ⊂ \subset S 终止状态集
在这里插入图片描述

正则表达式->NFA

汤姆逊算法(自底向上)(适合考试?)

是一种递归构造的算法,是对正则表达式做归纳。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

自顶而下的构造方法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

NFA->DFA(子集构造)

符号合并:对任意字符a,如果有S1 → a \overset{a}{\rightarrow} aS2, S1 → a \overset{a}{\rightarrow} aS3,则把对应
的状态S2和S3合并{S2, S3}。
ε合并:如果有S1→S2,则把状态S2合并至状态S1中
操作:
在这里插入图片描述
首先找初态(ε合并),初态可能是一个集合
然后对初态列出经过符号后的状态,每次出现新状态时,加入队列(状态这一列表示尚未处理的状态,DFA这一列表示所有出现的状态)
当没有新增状态时,算法结束。包含原终止状态的状态集是新的终止状态
此时可将状态集合用新字母表示(也可以画出DFA图,但没有必要,因为还有下一步)
共有三种表示方法:函数、状态转移矩阵、图

抽象化:
定义1:状态集合I的ε-闭包(ε-closure)
状态集ε-closure(I)称为I的ε-闭包
令I是一个状态集的子集,定义ε-closure(I) 为:
① 若s∈I,则:s∈ε-closure(I);
② 若s∈I,则:从s出发,经过任意条ε弧,能够到达的任何状态都属于ε-closure(I)

定义2:状态子集。令I是NFA M’状态集的一个子集,a∈Σ,定义:
Ia= ε-closure(J) ,其中J = ∪T(I,a)。 (所有I经过a能去的状态及它们经过ε能去的状态)
①J是从状态子集I中的每个状态出发,经过标记为a的弧而达到的
状态集合。(与符号合并相关)
② Ia是状态子集,其元素为J中的状态,加上从J中每一个状态出发
通过ε弧到达的状态。(与ε合并相关)

算法:队列(BFS)/ DFS
表驱动的算法

算法结束后,可以重命名状态集,然后画出图

DFA极小化 DFA->min DFA

对于任一个DFA,存在一个唯一的状态最少的等价的DFA
一个有穷自动机可以通过消除多余状态(死状态)和合并等价状态(划分不等价状态),转换成一个最小的与之等价的有穷自动机。
如何算法找出死状态?bfs所有的从初态开始的连通图 ,没有被遍历到的是死状态。
等价状态:
状态s和t的等价条件是:
① 一致性条件:状态s和t必须同时为可接受状态或不接受状态
② 蔓延性条件:对于所有输入符号,状态s和t必须转换到等价的状态里
对于所有输入符号c,Ic(s) = Ic(t),若状态s、t对于c具有相同的后继,则称s,t是等价的。
任何有后继的状态和任何无后继的状态一定不等价
有穷自动机的状态s和t不等价,则称这两个状态是可区别的

方法:

  • 分割法(自顶而下):把一个DFA(不含多余状态)的状态分
    割成一些不相关的子集,使得任何不同的两个子集状态都是可区别的,而同一个子集中的任何状态都是等价的
  • 状态集划分(自底向上):将所有DFA的终态与其它状态划分成两个子集G1与G2;分别从两个子集G1与G2(一般化:上次递归划分后的子集)中寻找等价状态进行化简
  • 在这里插入图片描述
    同时划分出好几个集合是可以的,不影响。
    思考:会不会出现划分完需要合并的情况?划分的次序会和顺序有关。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值