1.记号,模式与单词。
- 模式(pattern):产生/识别单词的规则
- 记号(token):按照某个模式(或规则)识别出的元素
- 单词(lexeme):被识别出的元素的值
2.词法分析器的作用
- 滤掉无用成分,比如注释,回车,空格
- 处理与具体平台有关的输入。比如文件结束符可能有不同表示,需要具体分析
- 识别记号并交给语法分析器
- 调用符号表管理器和出错处理器,进行相关处理
词法分析器的两种工作模式:(1) 作为语法分析器的子程序;(2) 词法分析器单独进行一遍扫描;(3) 与语法分析器并行的工作模式
3.字符串的基本概念
表示、术语 | 意义 |
|S| | 字符串S的长度 |
ε | 空串 |
Sⁿ | 表示S自身连接n次 (注意:S⁰=ε) |
S的前缀X | 去掉S尾部字符形成的字符串 (例如:S=“abc”,子串为"ε","a","ab","abc") |
S的后缀X | 类似上文 |
S的子串X | 类似上文 |
S的真前缀(后缀,子串)X | X是S的前缀/后缀/子串,X≠S且|X|>0 |
S的子序列X | 不同于子串,去掉的字符可以不连续 (例如:S="abc",X="ac") |
闭包运算:
- X=L* —— X是集合L的闭包,X=L⁰∪L¹∪L²∪L³.....
- X=L⁺ —— X是集合L的正闭包,X=L¹∪L²∪L³.....
4.正规式&正规集
定义:令Σ是一个有限字母表,则Σ上的 正规式 及其表示的集合递归定义如下:
1. ε是正规式,它表示集合 L(ε) = {ε}
2. 若a是Σ上的字符,则a是正规式,它表示集合L(a)={a}
3. 若正规式r和s分别表示集合L(r)和L(s),则
- r|s是正规式,表示集合L(r)∪L(s);表示或运算
- rs是正规式,表示集合L(r)L(s);表示连接运算
- r*是正规式,表示集合(L(r))*。表示闭包运算
示例:
- 括号可以规定运算的先后次序。
- 运算的高低优先级为:闭包 > 连接 > 或
- 若正规式子P,Q表示了同一个正规集,则称P和Q是等价的,记作P=Q
公理 | 公理 |
---|---|
r|s=s|r | (rs)t=r(st) |
r|(s|t)=(r|s)|t | εr=r=rε |
r(s|t)=rs|rt | r*=(r⁺|ε) |
(s|t)r=sr|tr | r**=r* |
简化正规式描述
- 一个或多个实例。正规表达式a⁺表示由-一个或多个a构成的所有串的集合。操作符⁺和操作符*具有同样的优先级和结合性。代数恒等式:r*=r|ε 和 r⁺=rr*。
- 零个或一个实例。一元后缀操作符?的意思是“零个或一个实例”。r?是r|ε的缩写形式。如果r是正规表达式,则(r)?是表示语言L(r)U{ε}的正规表达式。
- 字符类。[abc] (其中a、b和c是字母表中的符号)表示正规表达式alb|c。缩写的字符类[a-z]表示正规表达式alb...|z。
5.记号的识别
(1) 不确定的有限自动机 NFA
- 利用直观的方式表示有限自动机:状态转换图&状态转换矩阵
- NFA的特点是不确定性,在当前状态下可能有多个下一状态的转移。
- 从NFA的初态开始,对于输入序列的每一个字符,寻找它的下一状态转移,直到没有下一状态转移为止。若此时所处状态是终态,则识别,否则原路返回并探索下一路径。如果找不到下一状态,或者达不到终态,则输入序列不合法。
- 对于转换图,其中初态是没有前驱的节点,末态是加粗的圆圈或者双圆
- 对于转换矩阵其中第一行对应的状态为初态,末态需要特别指出
(2) 确定的有限自动机
- DFA是NFA的特例,在当前状态下,对于同一字符最多有一个下一状态转移。
(3) 有限自动机的等价
- 对于任何一个NFA,均可以找到一个与它等价的DFA。
6.Thompson算法构造NFA
7.子集法构造DFA
比如对于如图所示NFA,先构造:
A={0,1,2,4,7}
给A输入一个a,得到B={1,2,3,4,6,7,8}
给A输入一个b,得到C={1,2,4,5,6,7}
给B输入一个a,得到B
给B输入一个b,得到D={1,2,3,4,5,6,7,8,9}
给C输入一个a,得到B
给C输入一个b,得到C
给D输入一个a,得到B
给D输入一个b,得到E={1,2,3,4,5,6,7,8,9,10}
给E输入一个a,得到B
给E输入一个b,得到C
以输入为边,以ABCDE为定点,得到结果
8.最小化DFA的状态数
一开始划分成终态组与非终态组,然后以上题为例:
- 先划分成接受状态组(E)和非接受状态组(ABCD)
- 考虑(ABCD)。对于输入a,这些状态都转换到B,因此分组(ABCD)不变;但对于输人b, A、B和C都转换到状态组(ABCD)的一个成员,而D转换到另-组的成员E。于是状态组(ABCD)必须分裂成两个新组(ABC)和(D)。
- 对(ABC)输人a上仍然没有分裂,但对输人b. (ABC)还要划分,因为A和C都转到C.但B转到D,而C和D不在个组中,于是分成(AC)(B)(D)(E)。
- 现在只有(AC)有划分的可能。但是对于输入a, A和c都转换到B,对输人b,它们都转换到状态C,因而不必再划分。故结果为(AC)(B)(D)(E)
9.由DFA构造词法分析器
方法(1):将DFA转换图用矩阵表示,词法分析驱动代码一边接受字符一边查表。通常采用的最长匹配原则(比如“result:=”应该被识别成result而不是re或res)。以下是伪代码描述
char table[M][N];
table[0]['a']=1;
table[1]['b']=1;
table[1]['c']=1;//表中其他内容为ERR0R
nextToken(){
state=0;
stack=[];//空栈
while(state!=ERR0R){
c=getchar();
if(state is ACCEPT)clear(stack);
push(state);//给栈中送state
state=table[state][c];
} //end while
while(state is not ACCEPT){
state=pop();
rollback;
}//end while
}
方法(2):不需要表指导,直接用goto模拟DFA的行为。以下是伪代码描述,用例与上相同。
nextToken(){
state=0;
stack=[]
goto q0;
}
q0:
{
c=getchar();
if(state is ACCEPT)clear(stack);
push(state);
if(c=='a')goto q1;
}
q1:
{
c=getchar();
if(state is ACCEPT)clear(stack);
push(state);
if(c=='b'||c=='c')goto q1;
}