程序设计语言与编译原理-第六章词法分析课件_第1页
程序设计语言与编译原理-第六章词法分析课件_第2页
程序设计语言与编译原理-第六章词法分析课件_第3页
程序设计语言与编译原理-第六章词法分析课件_第4页
程序设计语言与编译原理-第六章词法分析课件_第5页
已阅读5页,还剩79页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第六章词法分析主要内容1.介绍词法分析的过程2.讨论词法分析器的设计与实现3.介绍实现词法分析器的主要工具:状态转换图第一节词法分析概述一.词法分析的功能1.功能扫描源程序的字符串,按照词法规则,识别出单词符号作为输出;对识别过程中发现的词法错误,则输出有关的错误信息。

功能:输入源程序,输出单词符号。单词符号是一个程序语言的基本语法符号。3词法分析器的功能:输入源程序,输出单词符号。单词符号:一个程序语言的基本语法符号。分为以下5种。

1、保留字(关键字):由程序语言定义的具有固定意义的标识符。也可称为保留字或基本字。例如:Pascal中的begin,end,if等。它是确定的。

2、标识符:用来表示各种名字,如变量名、数组名、过程名等。它是不限的。

3、常数:常数的类型一般有整型、实型、布尔型、文字型等。它是不限的。

4、运算符:如+、-、*、/等。它是确定的。

5、界符:如逗号、分号、括号、/*,*/等。它是确定的。确定不限二.词法分析器的输出形式5

(单词类别,单词的属性)区分单词所属的类(整数编码)单词的值2.单词的输出形式(1)二元式

单词符号的表示形式:词法分析器所输出的单词符号常常表示成

二元式(单词种别,单词自身的值)。单词种别可以用以下形式表示:

1、一类单词统一用一个整数值代表其属性。例如:1代表关键字,2代表标识符等。

2、每一个单词一个类别。例如:1代表BEGIN,2代表END等。单词自身的值可以表示成:常量的二进制表示;常量、变量等在符号表种的地址码,等等。(2)单词类别的划分基本字、运算符、界符:一字一码标识符:单列一种常数:按类型分类

一个例子A:=B50+10;的输出为:(标识符的编码,‘A’)(:=的编码,—)(标识符的编码,‘B50’)(+的编码,—)(整数的编码,‘10’)(;的编码,—)

注意:一个语言的单词符号如何分种,分几种,怎样编码,是一个技术问题。标识符一般同归为一种。常数则宜按类型(整、实、布尔)分。关键字可以将其全体视为一种,也可一字一种。运算符可采用一符一种,但也可把具有一定共性的视为一种。界符则一般采用一符一种。如何进行分种主要取决于处理上的方便。

若是一符一种分种,单词自身值就不需要了。否则,要查符号表。例:151-FORTRAN编译程序的词法分析器在扫描输入串

IF(5·EQ·M)GOTO100

后,它输出的单词符号串是:

逻辑IF(34,_)左括号(2,_)整常数(20,‘5’的二进制表示)等号(6,_)标识符(26,‘M’)右括号(16,_)

GOTO(30,_)标号(19,‘100’的二进制表示)IF为关键字,种别编码34,采用一符一种的编码方式。常数类型,种别编码20,单词自身的值为‘5’的二进制表示。‘(’为界符,种别编码2,采用一符一种的编码方式。等号为运算符,种别编码6,采用一符一种的编码方式。M为标识符,种别编码26,单词自身值为‘M’。‘)’为界符,种别编码16,采用一符一种的编码方式。GOTO为关键字,种别编码30,采用一符一种的编码方式。100为标号,种别编码19,单词内部的值用100的二进制表示。例

:下述C++代码段:while(i>=j)i--;经词法分析器处理以后,它将被转换为如下的单词符号串

(while,_)((,_)(id,指向i的符号表指针)(>=,_)(id,指向j的符号表指针)(),_)(id,指向i的符号表指针)(--,_)(;,_)词法分析程序的实现方式完全独立方式:词法分析程序作为单独一趟来实现。词法分析程序读入整个源程序,它的输出作为语法分析程序的输入。相对独立方式:把词法分析程序作为语法分析程序的一个独立子程序。语法分析程序需要新符号时调用这个子程序。2.词法分析器作为一个独立子程序12完全独立方式采用词法分析工作完全独立的原因:简化设计,降低语法分析的复杂性提高编译效率增加编译系统的可移植性源程序词法分析程序语法分析程序属性字序列….13相对独立方式当采用递归下降分析等技术实现一趟编译程序时常采用这种方式。词法分析器语法分析器符号表源程序单词符号取下一单词...14词法分析器的设计设计前提:

把scanner作为一个独立的子程序;词法分析器的任务为输出单词符号。预处理必要性:编辑性字符如空白符、回车符等,除了出现在文字和常数中以外,在别处出现都没有意义。功能:剔除无用字符。实现:预处理子程序。输入列表预处理子程序扫描器扫描缓冲区输入缓冲区单词符号图

词法分析器语法分析器预处理部分扫描器一.扫描缓冲区1.输入缓冲区:源程序输入缓冲区2.预处理程序:取消注解,剔除无用的空白、跳格、回车、换行等

3.扫描缓冲区:从输入缓冲区输入固定长度的字符串到另一个缓冲区(扫描缓冲区),词法分析可以直接在此缓冲区中进行符号识别。扫描缓冲区的结构:起点指针:用来指示正在扫描的单词的起点;搜索指针:用于向前搜索,寻找单词的结束;双缓冲区结构:设置左右两个缓冲区,当左缓冲区读完后,新读入的字符存入右缓冲区;反之,存放在左缓冲区;左缓冲区右缓冲区起点指示器搜索指示器

扫描缓冲区的大小应当保证单词符号不被缓冲区的边界打断扫描缓冲区使用一个一分为二的区域使用循环链表实现规定单词符号的大小不超过整个链表的容量起始指针搜索指针19二.符号的识别根据语言规定的词法规则,进行识别;对不同类型的单词符号,有不同的识别要求。超前搜索

词法分析程序在读取单词时,为了判断是否已读入整个单词的全部字符,常采取向前多读取字符并通过读取的字符来判别,即所谓超前搜索技术。(1)关键字识别:

例如:在标准FORTRAN中

1、DO99K=1,102、IF(5.EQ.M)I=103、DO99K=1.104、IF(5)=55

其中的DO、IF为关键字其中的DO、IF为标识符的一部分(2)标识符的识别:多数语言的标识符是字母开头的“字母/数字”串,而且在程序中标识符的出现后都跟着算符或界符。因此,不难识别。(3)常数的识别:根据常数的格式;大多数常数后都有运算符或界符。如FORTRAN的5.E08与5.EQ.M也必须超前搜索。(4)运算符的识别:需要超前搜索,对于诸如C++语言中的“++”、“--”,这种复合成的算符,需要超前搜索。(5)界符的识别:需要超前搜索,如/*

三状态转换图

是设计词法分析器的有效工具。转换图:是一张有限方向图。在状态转换图中,结点代表状态,用圆圈表示。状态之间用箭弧连接。箭弧上的标记(字符)代表在射出结状态下可能出现的输入字符或字符类。状态转换图的功能:用于识别一定的字符串。初态:一张转换图的启动条件,至少有一个,用圆圈表示。终态:一张转换图的结束条件,至少有一个,用双圈表示。*:表示多读进了一个字符。

1.结点(node):圆圈表示结点,代表状态(state)2.有向边(弧):连接结点,边上的标记字符表示该状态下可能接收或识别的字符;123xy状态转换图有限的有向图有向边上标记字符唯一初态若干终态(至少一个)123XY(a)转换图示例201字母其他字母或数字*(b)识别标识符的转换图其他201数字数字*(c)识别整数的转换图简单的状态转换图示例:初态终态从0状态到1状态可能出现字母7*65·数字101数字数字2数字3E或D+或-数字其他E或D·数字其他数字例:识别FORTRAN实型常数的转换图:例如下列实型常数可以被以下转换图识别:

1.23E+4.56E-7第四章设计的语言允许下述单词:标识符、数字串、begin、end、integer、if、

then、else、function、read、write、

-、*、<、<=、<>、

=、>、>=、:=、;、(、)词法分析器的设计与实现一.单词符号

单词符号种别编码助忆符内码值begin01$BEGIN-end02$END-integer03$INTEGER-if04$IF-then05$THEN-else06$ELSE-function07$FUNCTION-read08$READ-write09$WRITE-单词符号种别编码助忆符内码值标识符10$ID字符串常数11$INT二进制值=12$EQ-<>13$NE-<=14$LE-<15$LT->=16$GE>17$GT-

单词符号种别编码助忆符内码值-18$SUB-*19$MUL-:=20$ASSIGN-(21$LPAR-)22$RPAR-;23$SEM-

012字母字母/数字其它字符34数字数字非数字5=67<非=8=9>1011>非=12=13-14*1516:=17非=18(19)20;21其他退一字符;查保留字表;若是,返回(保留字种别,-)若不是,返回($ID,标识符在符号表中的位置)退一字符;返回($INT,常数在常数表中的位置)返回($EQ,-)返回($LT,-),退一字符返回($LE,-)返回($NE,-)返回($GT,-),退一字符返回($GE,-)返回($SUB,-)返回($MUL,-)返回($ASSIGN,-)出错处理返回($LPAR,-)返回($RPAR,-)返回($SEM,-)ERROR,非法字符二.状态转换图*****

为了把状态转换图转化成程序,每个状态要建立一段程序,它要做的工作如下:第一步:从输入缓冲区中取一个字符。为此,我们使用函数GETCHAR,每次调用它,推进先行指针,送回一个字符。第二步:确定在本状态下,哪一条箭弧是用刚刚来的输入字符标识的。如果找到,控制就转到该弧所指向的状态;若找不到,那么寻找该单词的企图就失败了。失败:先行指针必须重新回到开始指针处,并用另一状态图来搜索另一单词。如果所有的状态转换图都试过之后,还没有匹配的,就表明这是一个词法错误,此时,调用错误校正程序。GETCHAR是过程,将下一输入字符读入CHAR,搜索指示器前移一个字符。每个状态结对应一小段程序分支状态——if或case语句循环状态——while语句终态——return语句三.实现方法

33状态转换图的实现思想:每个状态结点对应一小段程序。做法:1)对不含回路的分叉结,可用一个CASE语句或一组IF-THEN-ELSE语句实现GetChar();if(IsLetter()){…状态j的对应程序段…;}elseif(IsDigit()){…状态k的对应程序段…;}elseif(ch=‘/’){…状态l的对应程序段…;}else{…错误处理…;}ijkl字母数字/342)对含回路的状态结,可对应一段由WHILE结构和IF语句构成的程序.i字母或数字其它jGetChar();while(IsLetter()orIsDigit()) GetChar();…状态j的对应程序段…3)终态结点表示识别出某种单词符号,因此,对应语句为

RETURN(C,VAL)

其中,C为单词种别,VAL为单词自身值.锻炼012字母其它字符字母或数字*数字23*其它符号

入口字母?取字符字母?数字?出口出口否是是否是否

9.查保留字子程序reserve10.语句return(c,val)11.二进制转换子程序dtb12.标识符存符号表子程序id13.指针变量val14.错误处理子程序error1.字符串变量char2.字符数组token3.读一字符子程序getchar4.删除空白子程序getnbc5.连接字符串子程序concat6.判定字母函数letter7.判定数字函数digit8.回退一字符子程序retract全局量及过程:用来存放最新读入的字符用来存放单词符号从扫描缓冲区读一个字符进入char,并将搜索指针移向下一个字符检查char中字符是否为空白;若是,调用getchar,直到char中的字符不是空白符把char中的字符连接到token布尔函数;若char中的字符为字母,返回真;否则,为假;布尔函数;若char中的字符为数字,返回真;否则,为假;将搜索指针回退一个字符,并把已读入char的字符用空白代替。用token中的字符串查保留字表,若查到,则返回该保留字的种别编码;否则返回0值其中c是种别编码,val或者是token在符号表中位置,或者是在常数表中的位置,或者无定义将token中的数字串转换成二进制值,并存入常数表中将token中的字符串存入符号表中存放标识符在符号表中的位置,或常数在常数表中的位置处理出现的词法错误start:token:=‘‘;getchar;getnb;casecharacterof‘a’…’z’:beginwhileletterordigitdobeginconcatenate;getcharend;retract;c:=reserve;ifc=0thenbeginbuildlist;return($ID,val)endelsereturn(c,—)end;‘0’…’9’:beginwhiledigitdobeginconcatenate;getcharend;retract;dtb;return($INT,val)end;‘=’:return($EQ,—);‘-’:return($SUB,—);‘*’:return($MUL,—);‘(’:return($LPAR,—);‘)’:return($RPAR,—);

‘<‘:begingetchar;ifcharacter=‘=‘thenreturn($LE,—)elseifcharacter=‘>’thenreturn($NE,—);retract;return($LT,—)end;‘>‘:begingetchar;ifcharacter=‘=‘thenreturn($GE,—);retract;return($GT,—)end;‘:‘:begingetchar;ifcharacter=‘=‘thenreturn($ASSIGN,—)elseerrorend;‘;‘:return($SEM,—)other:errorendofcase;gotostart;

将词法分析器实现为一个函数LexAnalyze(),函数每执行一次,就会从输入字符串中识别出一个单词符号并按二元式形式返回。实际词法分析中,可连续调用该函数,将输入字符串中的所有单词符号识别出来,并输出相应的二元式序列;也可将其作为语法分析程序的一个子程序,当语法分析需要下一个新单词时,就调用该函数,从输入字符串中识别一个单词后返回。

符号表42编译过程中编译程序需要不断汇集和反复查证出现在源程序中各种名字的属性和特征等有关信息。这些信息通常记录在一张或多张符号表中,每一项分两部分:名字(标识符)和此名字的有关信息。每个名字的有关信息一般指种属(如简单变量、数组、过程等)、类型(如整、实、布尔等)等等。这些信息将用于语义检查、产生中间代码以及最终生成目标代码等阶段。43在编译程序中符号表用来存放语言程序中出现的有关标识符的属性信息,这些信息集中反映了标识符的语义特征属性。在词法分析及语法在分析过程中不断积累和更新表中的信息,并在词法分析到代码生成的各阶段,按各自的需要从表中获取不同的属性信息。不论编译策略是否分趟,符号表的作用和地位是完全一致的。

①收集符号属性

②上下文语义的合法性检查的依据

③作为目标代码生成阶段地址分配的依据

44①收集符号属性

编译程序扫描说明部分收集有关标识符的属性,并在符号表中建立符号的相应属性信息。例如,编译程序分析到下述两个说明语句

intA;

floatB[5];

45上下文语义的合法性检查的依据同一个标识符可能在程序的不同地方出现,而有关该符号的属性是在这些不同情况下收集的。特别是在多趟编译及程序分段编译(在PASCAL及C中以文件为单位)的情况下,更需检查标识符属性在上下文中的一致性和合法性。通过符号表中属性记录可进行相应上下文的语义检查。

例如,在一个C语言程序中出现

inti[3,5];//定义整型数组i

floati[4,2];//定义实型数组i,重定义冲突

inti[3,5];//定义整型数组i,重定义冲突46③作为目标代码生成阶段地址分配的依据

每个符号变量在目标代码生成时需要确定其在存储分配的位置(主要是相对位置)。首先要确定其被分配的区域。在C语言中首先要确定该符号变量是分配在公共区(extern)、文件静态区(externstatic)、函数静态区(函数中static)、还是函数运行时的动态区(auto)等。其次是根据变量出现的次序决定该变量在某个区中所处的具体位置,这通常使用在该区域中相对区头的相对位置确定。而有关区域的标志及相对位置都是作为该变量的语义信息被收集在该变量的符号表属性中。47符号表的组织与作用在编译的各个分析阶段,每当遇到一个名字都要查找符号表。若是新名字或发现已有名字的新信息,则要加入(填入)。因此,合理组织符号表,使其存储占用最少,同时提高编译期间对符号表的访问效率,至关重要。一、符号表的作用48概括地说,符号表的每一项(或称入口)包含两大栏(或称区段、字域),即名字栏和信息栏。表格的形式如下:第1项(入口1)第2项(入口2)第n项(入口n)名字栏(NAME)信息栏(INFORMATION)…49信息栏包含许多子栏和标志位,记录相应名字的种种不同属性。由于查填符号表一般是通过匹配名字来实现的,故名字栏也称主栏,其内容称为关键字(keyword)。符号表中每一项都是关于名字的说明。因为所保存的关于名字的信息取决于名字的用途,所以各表项的格式不一定统一,为使表中的每个记录格式统一,可采用指针技术,在记录中设置指针,把某些信息放在表的外边,用指针指向存放另外信息的空间。50对符号表的操作大致可以分为五类:·查询某个名字是否已在表中。·填入一个新的名字。·添加或更新某个名字的某些信息。·删除一个或一组无用的项。·访问某个名字的某些信息。51对符号表进行操作的时机:定义性出现使用性出现按名字的不同种属建立多张符号表,如常数表、变量名表、过程名表、…符号的组织方式:1.安排各项各栏的存储单元为固定长度2.用间接方式安排各栏存储单元52最简单的组织方式:各项各栏长度固定——易于组织、填写和查找。这种表格,每一栏的内容可直接填写在有关的区段内。例如,对于名字栏,其大小可按标识符最大允许长度来确定。如标准FORTRAN语言规定每一标识符不得超过6个字符,因此就可用6个字符的空间作为名字栏的长度。若标识符长度不足6个字符,则以空白符补齐。二、符号表的组织方式53但是,有许多语言对标识符的长度几乎不加以限制,或者说,标识符的长度范围很宽。比如说,最长可允许由100个字符组成的名字。此时,如果名字栏的长度设为100个字符,则势必会大量浪费存储空间。因此,最好采用指针技术。用另外一独立的字符串数组,把所有标识符都存放其中。在符号表的主栏放一指示器和一整数,或仅放一指示器,同时在标识符前放一个整数。指示器指出标识符在字符串数组中的位置;整数代表此标识符的长度。54SAMPLELOOPINFORMATIONNAME,6,4在符号表的主栏放一指示器和一整数55INFORMATIONNAME6SAMPLE4LOOP在符号表的主栏放一指示器,同时在标识符前放一个整数56

类似地,如果各种名字所需的信息空间长短不一,则将共同属性直接登记在信息栏中,而特殊属性登记在别的地方。在信息栏中附设一指示器,指向存放特殊属性的地方。

例如对于数组标识符,需存储的信息包含维数等,如果将它们与其它名字全部集中在一张符号表中,处理起来很不方便。因此,常另辟一个信息表区,称数组信息表(或内情向量表)

,用来保存数组的有关信息。在符号表的地址栏内存入符号表与内情向量表连接的入口地址。当填写或查询数组有关信息时,通过符号表来访问此内情向量表。57

对过程名以及其它一些含信息较多的名字,都可类似地开辟专用信息表,存放那些不宜全部存放在符号表中的信息,而在符号表中保留与信息表相联系的地址信息。58

一张可容纳N项的符号表在存储器中可用下述两种不同方式之一表示(假定每项需用K个字)(1)把每一项置于连续的

K个字中。K*N个字表示。(2)分成M个子表T1,T2,…,Tm。每个子表含N项。假定子表Ti的每一项所需的字数为Ki

,则K=K1+K2+…+Km

。对于任何i,T1[i],T2[i],…,Tm[i]的并置就构成了符号表第i项的全部内容。59

编译过程中,每一遍所用的符号表可能略有差别。一般说来,主栏和某些基本属性栏大多不会改变,但另外一些信息栏可能在不同阶段有不同的内容,为了合理使用存储空间,特别是重新利用那些已经过时的信息栏所占用的空间,最好采用第二种存储表示方式。

若用高级语言实现的话,用记录数组或变体记录的数组来实现是比较合适的。60

在编译时,虽然从原则上说,使用一张统一的符号表就够了。但许多编译程序按名字的不同种属分别使用许多符号表。如常数表、变量名表、过程名表等等。这是因为,不同种属名字的相应信息往往不同,信息栏的长度也各有差异。因而,按不同种属建立不同的符号表在处理上常常是比较方便的。61例:PASCAL程序段:PROCEDUREINCWAP(M,N:INTEGER);LABELSTART;VARK:INTEGER;BEGINSTART:K:=M+1; M:=N+4; N:=K;END.62PROCEDUREINCWAP(M,N:INTEGER);LABELSTART;VARK:INTEGER;BEGINSTART:K:=M+1; M:=N+4; N:=K;END.63PROCEDUREINCWAP(M,N:INTEGER);LABELSTART;VARK:INTEGER;BEGINSTART:K:=M+1; M:=N+4; N:=K;END.64PROCEDUREINCWAP(M,N:INTEGER);LABELSTART;VARK:INTEGER;BEGINSTART:K:=M+1; M:=N+4; N:=K;END.65PROCEDUREINCWAP(M,N:INTEGER);LABELSTART;VARK:INTEGER;BEGINSTART:K:=M+1; M:=N+4; N:=K;END.6667PROCEDUREINCWAP(M,N:INTEGER);LABELSTART;VARK:INTEGER;BEGINSTART:K:=M+1; M:=N+4; N:=K;END.68§整理与查找构造符号表最简单、最容易的办法是按关键字出现的顺序依次填写各个项。用一个一维数组或多个一维数组来存放名字及有关信息。当碰到一个新名字时就按顺序将它填入表中,若需要了解一个名字的有关信息,则就从第一项开始顺序查找。这种构造方式称为线性表,其结构如下:(其中AVAILABLE总是指向空白区的首地址)一、线性表69项数1AVAILABLE234NAMEINFORMATIONJ1…XYZ…I…BC……70线性表中每一项的先后顺序是按先来者先填的原则安排的,编译程序不做任何整理次序的工作。如果是显式说明的程序设计语言,则根据各名字在说明部分出现的先后顺序填入表中(表尾);如果是隐式说明的程序设计语言,则根据各名字首次引用的先后顺序填入表中。当需要查找某个名字时,就从第一项开始顺序查找,若一直查到AVAILABLE还未找到这个名字,就说明该名字不在表中。71按照编程习惯,新定义的名字往往要立即使用。所以,反序查找效率也许更高。当需要填进一个新说明的名字时,必须先对这个名字进行查找,如果已在表中,则不重新填入(通常要报告重名错误)。如果不在表中,则填入AVAILABLE所指位置,然后累增AVAILABLE,使其指向下一空白项的单元地址。72对一张含N项的线性表,欲从中查找一项,则平均比较次数为n/2,所以效率很低,但由于结构简单且节省存储空间,所以许多编译程序仍采用。提高效率方法之一:每项附设一指示器,按“最新最近”访问原则,建立一链表,使得在任何时候,链的第一元素指向最新最近查询过的项——自适应线性表。73按名字大小建立有序表,则可采用对折查找法(折半查找)

最多比较次数1+log2N(对数查找)。要保持有序,需要每次填入新名字时都进行排序,因此采用二叉树组织法,左大右小。查找效率比对折查找低了一些,且所需存储空间增大,但其所需顺序化的时间要少得多,且比较次数仍和log2N成比例,因此可取。二、对折查找与二叉树74对表格处理,查表与填表都能高效进行是最理想的。线性表填表快、查表慢,而对折法填表慢、查表快,两方面综合——杂凑技术(哈希表或散列表):假定有一足够大的区域,可填写一张含N项的符号表,希望构造一个地址函数H,对任何名字SYM,H(SYM)都可获得一个0和N-1之间的值。也就是说,不论对SYM是查表还是填表,都希望通过H(SYM)获得它在表中的位置。→杂凑函数,哈希函数。三、杂凑技术75假设用无符号整数作为项名,若取N为质数,则H

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论