编译原理(三)--词法分析

第三章 词法分析

图片的制作可能因为Markdown自身原因,箭头有一些都不见了

3.1 词法分析程序的设计

源程序
词法分析器
单词序列

主要任务:
从左向右逐行扫描源程序字符,识别出各个单词,确定单词的类型,将识别出的单词转化成统一的机内表示–词法单元形式
token <种别码,属性值>

单词类型种类识别码
关键字program,if,else,then…一词一码
识别符变量名,数组名,记录名,过程名多词一码
常量整形,浮点型,字符型,布尔型一型一码
运算符算数(+,-,*,/)关系(> < == != >= <= ) 逻辑(& | ~)一词一码或一型一码
界限符;() = { }一词一码

步骤:

  1. 组织源程序的输入
  2. 识别单词,转换成机内表示形式
  3. 删除注释行,空格及无用符号
  4. 查填符号表
  5. 检查词法错误

词类和属性:

程序语言单词的分类

1.关键字(保留字或基本字):while,if

2.标识符:用来表示各种名字

3.字面常数:256,3.14,true,‘abc’

4.运算符:+,-,*,/

5.分界符:逗号,分号,冒号

词法分析器的输出:

(词类编码,单词自身的属性值)

词类编码原则:

界符和运算符:一符一码

关键字可分为一类,也可以一个关键字分成一类:一字一码

常数可统归一类,也可以按类型(整型、实型、布尔型等),每个类型的常数划分成一类:一类型一码

所有的标识符分为一类:一类一码

对于关键字、界符、运算符来说,它们的词类编码就可以表示其完整的信息,故对于这类单词,其单词自身的属性值通常为空。

而对于标识符,词类编码所反映的信息不够充分,标识符的具体特性还要通过单词自身的属性进行互相区分。标识符的单词自身的属性常用其在符号表中的入口指针来表示

对于常数, 其单词自身的属性常用其在常数表中的入口指针来表示

词法分析的设计形式:

1.设计成一个独立程序,完成词法分析的任务,结果以文件的形式组织,做为语法分析的输入

源程序
词法分析
符号表
token字
错误信息

2.作为语法分析和语义分析的子程序

错误的诊察处
语法分析
词法分析
语义分析和中间代码生成
源程序
中间代码
符号表管理

3.2 词法分析器的手工构造

为了构造词法分析器,要研究构词法,每种词类的结构模式以及识别它的数学模型——有穷自动机

3.2.1 手工构造识别单词的DFA m

根椐DFA识别单词的定义,在研究给定程序语言单词结构的基础上,能直接构造出识别它的DFA m。

3.2.2 编写词法分析程序

根据画出的状态转换图(识别单词的)构造词法分析程序,每个状态对应一段程序,完成到达此状态的工作;

词法分析程序的控制程序模拟状态转换图的状态转换。

在识别标识符的过程中,首先要拼写出来,并和保留字区别开来;识别出的标识符要填入符号表中

在识别常数的过程中,要把它转换成机器表示以作为属性值,记录到常数表中。

3.3 词法分析程序的自动构造工具LEX简介

原理:

单词的结构用正规式描述
正规式 ⟶ \longrightarrow NFA ⟶ \longrightarrow DFA ⟶ \longrightarrow min DFA

用LEX建立词法分析程序的过程:

LEX源程序lex.1
LEX编译器

lex源程序

  1. 声明
  2. 翻译规则
  3. 辅助过程

声明包括变量,符号常量和正规定义式。
翻译规则的形式为:

   p1              { 动作1}
   p2              { 动作2}
  
  每个pi是正规定义式的名子,每个{动作i}是正规定义式pi识别某类单词时,词法分析器应执行动作的程序段。
  
  **辅助过程**是动作需要的,这些过程用C书写,可以分别编译.例:LOOKUP()

3.4 正则表达式

**正则表达式:**是一种用来描述正则语言的更精凑的表示方法

例:r=a(a|b)*( ϵ \epsilon ϵ|(.| )(a|b)(a|b)*)

正则表达式可以由较小的正则表达式按照特定的递归地构建,每个正则表达式r定义(表示)一个语言,记为L(r)这个语言也是根据r的子表达式所表示的语言递归定义的

正则表达式的定义

ϵ \epsilon ϵ是一个RE,L( ϵ \epsilon ϵ)={ ϵ \epsilon ϵ}

如果a ∈ ∑ \in\sum ,则a是一个RE,L(a)={a}

假设r和s都是RE,表示的语言分别是L(r)和L(s),则

  1. r|s是一个RE,L(r|s)=L®UL(s)
  2. rs是一个RE,L(rs)=L®L(s)
  3. r ∗ r^* r是一个RE,L( r ∗ r^* r)= ( L ( r ) ) ∗ ) (L(r))^*) (L(r)))
  4. ®是一个RE,L(®)=L®

运算的优先级:*,连接,|

可以用RE定义的语言叫做正则语言或正则集合

正则文法与正则表达式等价

对任意正则文法G,存在定义同一个语言的正则表达式r

对任意正则表达式r,存在生成同一语言的正则文法G

3.5 正则定义

正则定义是具有如下形式的定义序列:
d 1 ⟶ r 1 d_1\longrightarrow r_1 d1r1

d 2 ⟶ r 2 d_2\longrightarrow r_2 d2r2

d n ⟶ r n d_n\longrightarrow r_n dnrn

其中:

每个 d i d_i di都是一个新符号,他们都不在字母表 ∑ \sum 中,而且各不相同

每个 r i r_i ri是字母表 ∑ \sum U{ d 1 d 2 , . . . d i − 1 d_1d_2,...d_{i-1} d1d2,...di1}上的正则表达式

例:

C语言中标识符的正则定义

d i g i t ⟶ 0 ∣ 1 ∣ 2 ∣ . . . ∣ 9 digit\longrightarrow0|1|2|...|9 digit012...9

l e t t e r _ ⟶ A ∣ B ∣ C ∣ . . . ∣ Z ∣ a ∣ b ∣ z letter\_ \longrightarrow A|B|C|...|Z|a|b|z letter_ABC...Zabz

i d ⟶ l e t t e r _ ( l e t t e r _ ∣ d i g i t ) ∗ id\longrightarrow letter\_(letter\_|digit)^* idletter_(letter_digit)

3.6 有穷自动机

有穷自动机是对一类处理系统建立的数学模型

这类系统具有一系列离散的输入输出信息和有穷数目的内部状态

系统只需要根据当前所处状态当期面临的输入信息就可以决定系统的后继行为。每当系统处理了当前的输入后,系统的内部状态也将发生改变

FA模型

输入带:用来存放输入符号串

读头:从左向右逐个读取输入符号,不能修改(只读,不能往返运动)

有穷控制器:具有有穷个状态数,根据当前的状态和当前输入符号控制转入下一状态

FA的表示

转换图

结点:FA的状态

初始状态(开始状态):只有一个,由start箭头指向

终止状态(接收状态):可以有多个,用双圈表示

带标记的有向边:如果对于输入a,存在一个状态p到状态q的转换,就在p,q之间画一条有向边,并标记上a

FA定义(接收)的语句

给定输入串x,如果存在一个对应于串x的从初始状态到某个中止状态的转换序列,则称串x被该FA接收

由一个有穷自动机M接收的所有串构成的集合称为是该FA定义(或接收)的语言,记为L(M)

最长子串匹配原则

当输入串的多个前缀与一个或多个模式匹配时,总是选择最长的前缀进行匹配

到达某个终态之后,只要输入带上还有符号,FA就继续前进,以便寻找尽可能长的匹配

3.7 有穷自动机的分类

确定的FA(Deterministic finite automata,DFA)

非确定的FA(Nondeterministic finite autommata,NFA)

3.7.1 确定的有穷自动机(DFA)

M = ( S , ∑ , δ , s 0 , F ) M=(S,\sum,\delta,s_0,F) M=(S,,δ,s0,F)

S:有穷状态集

∑ \sum :输入字母表,即输入符号集合。假设 ϵ \epsilon ϵ不是 ∑ \sum 中的元素

δ \delta δ:将S × ∑ \times\sum ×映射到S的转换函数。 ∀ s ∈ S , a i n ∑ , ϵ ( s , a ) \forall s\in S,a in \sum,\epsilon(s,a) sS,ain,ϵ(s,a)表示从状态s出发,沿着标记为a的边所能达到的状态

s 0 s_0 s0:开始状态(或初始状态), s 0 ∈ S s_0 \in S s0S

F:接收状态(或终止状态)集合, F ⊂ S F \subset S FS

一个DFA,可以用转换图来表示,也可以用转换表表示

3.7.2 非确定的有穷自动机(NFA)

M = ( S , ∑ , δ , s 0 , F ) M=(S,\sum,\delta,s_0,F) M=(S,,δ,s0,F)

S:有穷状态集

∑ \sum :输入字母表,即输入符号集合。假设 ϵ \epsilon ϵ不是 ∑ \sum 中的元素

δ \delta δ:将S × ∑ \times\sum ×映射到 2 S 2^S 2S的转换函数。 ∀ s ∈ S , a i n ∑ , ϵ ( s , a ) \forall s\in S,a in \sum,\epsilon(s,a) sS,ain,ϵ(s,a)表示从状态s出发,沿着标记为a的边所能达到的状态集合

s 0 s_0 s0:开始状态(或初始状态), s 0 ∈ S s_0 \in S s0S

F:接收状态(或终止状态)集合, F ⊂ S F \subset S FS

一个NFA,可以用转换图来表示,也可以用转换表表示

如果转换函数没有给出对应于某个状态-输入对的信息,就把 ϕ \phi ϕ放入对应表项中

3.7.3 DFA和NFA的等价性

对任何NFA N,存在识别同一语言的DFA D

对任何DFA D,存在识别同一语言的NFA N

3.7.4 带有‘’ ϵ \epsilon ϵ-边 ‘’的NFA

M = ( S , ∑ , δ , s 0 , F ) M=(S,\sum,\delta,s_0,F) M=(S,,δ,s0,F)

S:有穷状态集

∑ \sum :输入字母表,即输入符号集合。假设 ϵ \epsilon ϵ不是 ∑ \sum 中的元素

δ \delta δ:将S$\times(\sum U U U{\epsilon})$映射到 2 S 2^S 2S的转换函数。 ∀ s ∈ S , a i n ∑ , ϵ ( s , a ) \forall s\in S,a in \sum,\epsilon(s,a) sS,ain,ϵ(s,a)表示从状态s出发,沿着标记为a的边所能达到的状态集合

s 0 s_0 s0:开始状态(或初始状态), s 0 ∈ S s_0 \in S s0S

F:接收状态(或终止状态)集合, F ⊂ S F \subset S FS

3.7.5 DFA算法实现

**输入:**以文件结束符eof结尾的字符串x,DFA D的开始状态 s 0 s_0 s0,接收状态集F,转换函数move

**输出:**如果D接收x,则回答“yes”,否则回答“no“

**方法:**将下述算法应用于输入串x

s=s0;
C=nextChar();
while(c!=eof){
  s=move(s,c);
  c=nextChar();
}
if(s在F中) return "yes";
else return "no";

函数nextChar()返回输入串x的下一个符号

函数move(s,c)表示从状态s出发,沿着标记为c的边能达到的状态

3.7.6 从正则表达式到有穷自动机

RE
DFA
NFA

根据RE构造NFA

构造NFA到DFA的转换:子集构造法

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
附录c 编译程序实验 实验目的:用c语言对一个简单语言的子集编制一个一遍扫描的编译程序,以加深对编译原理的理解,掌握编译程序的实现方法和技术。 语法分析 C2.1 实验目的 编制一个递归下降分析程序,实现对词法分析程序所提供的单词序列的语法检查和结构分析. C2.2 实验要求 利用C语言编制递归下降分析程序,并对简单语言进行语法分析. C2.2.1待分析的简单语言的语法 实验目的 通过上机实习,加深对语法制导翻译原理的理解,掌握将语法分析所识别的语法成分变换为中间代码的语义翻译方法. 实验要求 采用递归下降语法制导翻译法,对算术表达式、赋值语句进行语义分析并生成四元式序列。 实验的输入和输出 输入是语法分析提供的正确的单词串,输出为地址指令形式的四元式序列。 例如:对于语句串 begin a:=2+3*4;x:=(a+b)/c end# 输出的地址指令如下: (1) t1=3*4 (2) t2=2+t1 (3) a=t2 (4) t3=a+b (5) t4=t3/c (6) x=t4 算法思想 1设置语义过程 (1) emit(char *result,char *arg1,char *op,char *ag2) 该函数功能是生成一个地址语句送到四元式表中。 四元式表的结构如下: struct {char result[8]; char ag1[8]; char op[8]; char ag2[8]; }quad[20]; (2)char *newtemp() 该函数回送一个新的临时变量名,临时变量名产生的顺序为T1,T2,…. Char *newtemp(void) { char *p; char m[8]; p=(char *)malloc(8); k++; itoa(k,m,10); strcpy(p+1,m); p[0]=’t’; return(p); } (2)主程序示意图如图c.10所示。 (2) 函数lrparser在原来语法分析的基础上插入相应的语义动作:将输入串翻译成四元式序列。在实验中我们只对表达式、赋值语句进行翻译。 语义分析程序的C语言程序框架 int lrparser() { int schain=0; kk=0; if(syn=1) { 读下一个单词符号; schain=yucu; /调用语句串分析函数进行分析/ if(syn=6) { 读下一个单词符号; if(syn=0 && (kk==0)) 输出(“success”); } else { if(kk!=1 ) 输出 ‘缺end’ 错误;kk=1;} else{输出’begin’错误;kk=1;} } return(schain); int yucu() { int schain=0; schain=statement();/调用语句分析函数进行分析/ while(syn=26) {读下一个单词符号; schain=statement(); /调用语句分析函数进行分析/ } return(schain); } int statement() { char tt[8],eplace[8]; int schain=0; {switch(syn) {case 10: strcpy(tt,token); scanner(); if(syn=18) {读下一个单词符号; strcpy(eplace,expression()); emit(tt,eplace,””,””); schain=0; } else {输出’缺少赋值号’的错误;kk=1; } return(schain); break; } } char *expression(void) {char *tp,*ep2,*eplace,*tt; tp=(char *)malloc(12);/分配空间/ ep2=(char *)malloc(12); eplace=(char *)malloc(12); tt =(char )malloc(12); strcpy(eplace,term ());/调用term分析产生表达式计算的第一项eplace/ while(syn=13 or 14) { 操作符 tt= ‘+’或者‘—’; 读下一个单词符号; strcpy(ep2,term());/调用term分析产生表达式计算的第二项ep2/ strcpy(tp,newtemp());/调用newtemp产生临时变量tp存储计算结果/ emit(tp,eplace,tt,ep2);/生成四元式送入四元式表/ strcpy(eplace,tp); } return(eplace); } char *term(void)/仿照函数expression编写/ char *factor(void) {char *fplace; fplace=(char *)malloc(12); strcpy(fplace, “ ”); if(syn=10) {strcpy(fplace,,token);/将标识符token的值赋给fplace/ 读下一个单词符号; } else if(syn=11) {itoa(sum,fplace,10); 读下一个单词符号; } else if (syn=27) {读下一个单词符号; fplace=expression();/调用expression分析返回表达式的值/ if(syn=28) 读下一个单词符号; else{输出‘}’错误;kk=1; } } else{输出‘(’错误;kk=1; } return(fplace); }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值