PL0语言的词法语法分析器的设计与实现_第1页
PL0语言的词法语法分析器的设计与实现_第2页
PL0语言的词法语法分析器的设计与实现_第3页
PL0语言的词法语法分析器的设计与实现_第4页
PL0语言的词法语法分析器的设计与实现_第5页
已阅读5页,还剩53页未读 继续免费阅读

下载本文档

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

文档简介

PAGE目录TOC\o\h\z\u1前言 12PL/0语言描述 12.1PL/0语言的语法描述图 22.2PL/0语言文法的EBNF表示 33PL/0语言的词法与语法分析 43.1PL/0编译器的词法分析 43.2PL/0编译器的语法分析 73.2.1PL/0编译器语法分析过程的直观思想 73.2.2PL/0编译器语法分析程序分析 84PL/0语言词法与语法分析器的设计 105PL/0语言词法与语法分析器的编程实现 115.1编程环境简介 115.1.1系统硬件设备 115.1.2系统支持软件 115.2所采用的关键技术介绍 115.2.1窗口初始化 115.2.2切分窗口 125.2.3位图的显示 135.3具体功能的实现 145.3.1菜单的设计与实现 145.3.2窗体分割的设计与实现 155.3.3弹出式对话框的设计与实现 165.3.4工具条的设计 165.3.5起始屏的设计 185.4PL/0语言词法与语法分析器的测试 195.4.1测试的重要性 195.4.2分析器的测试过程和运行效果 196结束语 22参考文献 22附录 23PL/0语言的词法语法分析器的设计与实现摘要:编译器是现代计算机系统的基本组成部分之一,它把源语言程序翻译成目标语言程序。词法分析和语法分析是编译器的两个重要功能。词法分析的任务是对构成源程序的字符流进行扫描和分解,从而识别出单词序列;语法分析的任务是将词法分析得到的单词序列分解成各类语法短语。本文在介绍PL/0语言编译器的词法分析和语法分析过程的基础上,具体阐述了利用VC++作为编程工具完成PL/0语言词法与语法分析器的设计与实现的过程。关键词:编译器;源语言;目标语言;词法分析;语法分析DesigningandImplementingtheLexicalAnalyzerandSyntaxAnalyzerofPL/0LanguageAbstract:Compilerisabasicpartofmoderncomputersystem,ittranslatesthesourcelanguageprogramintothetargetlanguageprgram.Lexicalanalysisandsyntaxanalysisaretwoimportantfunctionofthecompiler.Theformercompletesthetaskofscanninganddecomposingthestreamofcharactersmakingupthesourceprogramandidentifyingsequencesofwords.Thelattercompletesthetaskofclassingthestreamofwordsintosyntaxphrases.Inthispaper,onthebasisofintroducingthecoursesoflexicalanalysingandsyntaxanalyzingofofPL/0Languagecompiler,thedesignandimplementationprocessofusingVisualC++asaprogrammingtooltocompletethelexicalanalyzerandsyntaxanalyzerofPL/0Languageisconcretelydescribed.Keywords:Compiler;Sourcelanguage;Targetlanguage;Lexicalanalysis;SyntaxanalysisPAGE221前言编译器是现代计算机系统的基本组成部分之一,而且多数计算机系统都含有不止一个高级语言的编译器,对有些高级语言甚至配置了几个不同性能的编译器。从功能上看,一个编译器就是一个语言翻译程序。它把一种语言(称作源语言)书写的程序翻译成另一种语言(称作目标语言)的等价的程序。比如汇编程序是一个翻译程序,它把汇编语言程序翻译成机器语言程序。如果源语言是像FORTRAN,PASCAL,或C那样的高级语言,目标语言是像汇编语言或机器语言那样的低级语言,则这种翻译程序称作编译程序或编译器[3]。词法分析是编译过程的第一个阶段,这个阶段的作用是从左到右一个字符一个字符地读入源程序,对构成源程序的字符流进行扫描和分解,从而识别一个个单词。源程序中常见的单词可以归为几大类:关键字、标识符、字面量和特殊符号。语法分析就是编译过程的第二个阶段,它的作用是在词法分析的基础上将单词序列分解成各类语法短语,如“程序”,“语句”,“表达式”等等。所以语法分析器的任务是在词法分析的基础上对源程序的语法短语进行分析,找出其中的错误并且对用户发出警告[5]。文献[8]中指出,PL/0语言具有功能简单、结构清晰、可读性强的特点,且具备了一般高级语言的必须部分。PL/0语言的编译器能充分体现一个高级语言编译器实现的基本技术和步骤。因此,本次毕业设计在对PL/0语言编译器的词法分析过程与语法分析过程进行深入分析研究的基础上,采用VC++编程来实现PL/0语言的词法与语法分析器。以通过对PL/0语言编译器中的词法分析和语法分析程序的学习和词法语法分析器的设计,达到深入理解编译原理及熟练掌握编程语言的目的。2PL/0语言描述PL/0语言是PASCAL语言的一个子集[3],主要用于处理整型数值间的加减乘除和赋值等各种简单运算以及各种关系运算,它的功能简单、结构清晰、可读性强、而又具备了一般高级语言的必须部分,PL/0语言的编译器能充分体现一个高级语言编译器实现的基本技术和步骤,对建立实现编译器的整体概念有很大帮助。以下为一个PL/0示例程序:Programabc;/*以下是程序体*/Varx,y,z;/*变量声明*/Consta=10;/*常量声明*/Procedureab(integerm,n:Var;t:Const);/*过程声明*/Begint:=n+m;Ifm>nthen/*条件语句*/n:=telsem:=t;Whilen<mdo/*循环语句*/Beginn:=n+1EndEnd;Beginx:=1;y:=2;z:=3;Callab(x,y,z);z:=4+z+(1+2+6+x)*3*y*5+7;If~3<=x&y<>4thenz:=0;/*条件表达式*/y:=219;z:=37;x:=a*z/*赋值语句*/End./*程序结束符*/根据示例程序可以看到PL/0语言鲜明的特点,为了更清楚的了解它的语法特点,下面分别用语法描述图和EBNF来表示。2.1PL/0语言的语法描述图PL/0语言的语法规则可用语法描述图来描述,用语法图描述语法规则具有直观、易读的优点。在语法图中,用椭圆和圆圈中的英文字表示终结符,用长方形内的中文字表示非终结符。所谓终结符,是构成语言文法的单词,是语法成分的最小单位。而每个非终结符是一个语法成分,在书写语言程序时并不出现,它是由终结符和非终结符、或终结符串定义的[11]。例如,图2-1中,程序是由非终结符‘分程序’和终结符“.”这个串定义的。由于对某些非终结符可以递归定义,这就使得无穷的句子集可用有穷的文法描述。而通常称第一个非终结符如‘程序’为文法的开始符号。如图2-2所示为分程序的语法描述图。分程序分程序.程序图2-1程序语法描述图图2-2分程序语法描述图2.2PL/0语言文法的EBNF表示PL/0语言的语法描述也可用扩充的巴科斯-瑙尔范式[3]表达如下:(1)EBNF表示的符号说明<>:用左右尖括号括起来的中文字表示语法构造成分,或称语法单位,为非终结符。‘::=’:该符号的左部由右部定义,可读作‘定义为’。‘|’:表示‘或’,为左部可由多个右部定义。‘{}’:表示花括号内的语法成分可以重复。在不加上下界时可重复0到任意次数,在有上下界时为可重复次数的限制。‘[]’:表示方括号内的成分为任选项。‘()’:表示圆括号内的成分优先。(2)PL/0语言文法的EBNF表示<程序>::=<分程序>.<分程序>::=[<常量说明部分>][<变量说明部分>][<过程说明语句>]<语句><常量说明部分>::=CONST<常量定义>{,<常量定义>};<常量定义>::=<标识符>=<无符号整数><无符号整数>::=<数字>{<数字>}<变量说明部分>::=VAR<标识符>{,<标识符>};<标识符>::=<字母>{<字母>|<数字>}<过程说明部分>::=<过程首部><分程序>{;<过程说明部分>};<过程首部>::=PROCEDURE<标识符>;<语句>::=<赋值语句>|<条件语句>|<当型循环语句>|<过程调用语句>|<读语句>|<写语句>|<复合语句>|<空><赋值语句>::=<标识符>:=<表达式><复合语句>::=BEGIN<语句>{;<语句>}END<条件>::=<表达式><关系运算符><表达式>|ODD<表达式><表达式>::=[+|-]<项>{<加法运算符><项>}<项>::=<因子>{<乘法运算符><因子>}<因子>::=<标识符>|<无符号整数>|‘(’<表达式>‘)’<加法运算符>::=+|-<乘法运算符>::=*|/<关系运算符>::==|#|<|<=|>|>=<条件语句>::=IF<条件>THEN<语句><过程调用语句>::=CALL<标识符><当型循环语句>::=WHILE<条件>DO<语句><读语句>::=READ‘(’<标识符>{,<标识符>}‘)’<写语句>::=WRITE‘(’<表达式>{,<表达式>}‘)’<字母>::=a|b|…|X|Y|Z<数字>::=0|1|2|…|8|93PL/0语言的词法与语法分析3.1PL/0编译器的词法分析文献[12]中指出,PL/0编译器的词法分析程序可以看成是一个独立的过程,其功能是为语法分析提供单词,是语法分析的基础,它把输入的字符串形式的源程序分割成一个个单词符号。为此PL/0编译器设置了三个全程量的公用单元如下:(1)SYM:存放每个单词的类别,用内部编码形式表示。(2)ID:存放用户所定义的标识符的值。即标识符字符串的机内表示。(3)NUM:存放用户自定义的数。PL/0语言单词的种类有五种。(1)基本字:也可称为保留字,如BEGIN、END、IF、THEN等。(2)运算符:如+、-、*、/、:=、#、>=、<=等。(3)标识符:如用户定义的变量名、常数名、过程名。(4)常数:如10、25、100等整数。(5)界符:如‘、’、‘.’、‘;’、‘(’、‘)’等。如果把基本字、运算符、界符称为语言固有的单词,而对标识符、常数称为用户定义的单词。那么经词法分析程序分解出的单词,对语言固有的单词只给出类别存放在SYM中,而对用户定义的单词(标识符或常数)既给出类别又给出值,其类别放在SYM中,值放在ID或NUM中,全部单词种类由编译器定义的SYMBOL给出,也可称为语法的词汇表。如下面提到的IFSYM,THENSYM,IDENT,NUMBER均属SYMBOL中的元素。因此,PL/0编译器的词法分析过程将完成下列任务:(1)滤空格:空格在词法分析时是一种不可缺少的界符,而在语法分析时则是无用的所以必须滤掉。(2)识别保留字:设有一张保留表。对每个字母打头的字母、数字字符串要查此表。若查着则为保留字,将对应的类别放在SYM中。如IF对应值IFSYM,THEN对应值THENSYM。若查不到,则认为是用户定义的标识符。(3)识别标识符:对用户定义的标识符将IDENT放在SYM中,标识符本身的值放在ID中。(4)拼数:当所取单词是数字时,将数的类别NUMBER放在SYM中,数值本身的值存放在NUM中。(5)拼复合词:对两个字符组成的算符如:>=、:=、<=等单词,识别后将类别送SYM中。(6)输出源程序:为边读入字符边输出(可输出在文件中)。由于一个单词往往是由一个或几个字符组成的,所以在词法分析过程CIFAFENXI中又定义了一个取字符过程GETCH,由词法分析需要取字符时调用。根据以上分析,可得词法分析过程CIFAFENXI的流程图如图3-1所示。图3-1词法分析过程CIFAFENXI取字符过程GETCH的流程图如图3-2所示。图3-2取字符过程GETCH图中GETCH所用单元说明如下:CH:存放当前读取的字符,初值为空。LINE:为一维数组,其数组元素为字符,界对为1:80。用于读入一行字符的缓冲区。LL和CC为计数器,初值为0。CIFAFENXI流程图的工作单元说明:A:一维数组,数组元素为字符,界对[1:10]。ID:同A。WORD:保留字表,一维数组,数组元素是以字符为元素的一维数组。界对为[1:13]。查表采用二分法。根据上述描述定义一个词法分析子程序,命名为getsym,其功能是从源程序中读出一个单词符号(token),把它的信息放入全局变量sym、id和num中,语法分析器需要单词时,直接从这三个变量中获得。(注意!语法分析器每次用完这三个变量的值就立即调用getsym子程序获取新的单词供下一次使用。而不是在需要新单词时才调用getsym过程。)getsym过程通过反复调用getch子过程从源程序过获取字符,并把它们拼成单词。getch过程中使用了行缓冲区技术以提高程序运行效率。调用getsym时,它通过getch过程从源程序中获得一个字符。如果这个字符是字母,则继续获取字符或数字,最终可以拼成一个单词,查保留字表,如果查到为保留字,则把sym变量赋成相应的保留字类型值;如果没有查到,则这个单词应是一个用户自定义的标识符(可能是变量名、常量名或是过程的名字),把sym置为ident,把这个单词存入id变量。查保留字表时使用了二分法查找以提高效率。如果getch获得的字符是数字,则继续用getch获取数字,并把它们拼成一个整数,然后把sym置为number,并把拼成的数值放入num变量。如果识别出其它合法的符号(比如:赋值号、大于号、小于等于号等),则把sym置成相应的类型。如果遇到不合法的字符,把sym置成null。3.2PL/0编译器的语法分析文献[12]中指出,语法分析的任务是识别由词法分析给出的单词符号序列在结构上是否符合给定的文法规则。PL/0语言的文法规则已在前面给出,本节将以语法图描述的语法形式为依据,给出语法分析的直观思想。3.2.1PL/0编译器语法分析过程的直观思想PL/0编译器的语法分析采用了自顶向下的递归子程序法[3]。粗略地说,就是对应每个非终结符语法单元,编一个独立的处理过程(或子程序)。语法分析从读入第一个单词开始由非终结符‘程序’即开始符出发,沿语法描述图箭头所指出的方向进行分析,当遇到非终结符时,则调用相应的处理过程,从语法描述图看,也就是进入了一个语法单元,再沿当前所进入的语法描述图的箭头方向进行分析,当遇到描述图中是终结符时,则判断当前读入的单词是否与图中的终结符相匹配,若匹配则说明符合语法规则;再读取下一个单词继续分析。遇到分支点时将当前的单词与分支点上的多个终结符逐个相比较,若都不匹配时可能是进入下一非终结符语法单位或是出错。文献[8]中指出,如果一个PL/0语言的单词序列在整个语法分析中,都能逐个得到匹配,直到结束符‘.’,这时说明所输入的程序是正确的。图3-3语法调用关系程序分程序图3-3语法调用关系程序分程序语句条件表达式项因子每个产生式的右部都由终结符号开始。如果两个产生式有相同的左部,那么它们的右部由不同的终结符开始。对于这样的文法显然在推导过程中完全可以根据当前的输入符号决定选择哪个产生式往下推导,因此分析过程是唯一确定的。此外,从PL/0的语法描述图中可以清楚的看到,当对PL/0语言进行语法分析时,各个非终结符语法单元所对应的分析过程之间必须存在相互调用的关系。这种调用关系可用图3-3表示。也可称为PL/0语法的依赖图,在图中箭头所指向的程序单元表示存在调用关系,从图中不难看出这些子程序在语法分析时被直接调用或间接递归调用。3.2.2PL/0编译器语法分析程序分析语法分析主要完成两大功能:说明部分的处理和程序体的处理[3]。程序体就是语法单元中的分程序部分,通常把它称为过程体。语法分析由过程YUFAFENXI完成,其流程如图3-4所示。YUFAFENXIYUFAFENXI为DX,TX置初值,暂时保留CODE的下标指针CX值在TABLE表中SYM=CONSTSYM?常量说明处理SYM=VARSYM?变量说明处理SYM=PROCSYM?在TABLE表中登记过程名递归调用YUFAFENXICIFAFENXI出错处理返回是否为语句开始符在TABLE表中返填过程体入口图3-4YUFAFENXI过程的流程图过程YUFAFENXI内对程序说明部分和程序体部分的处理如下:(1)说明部分由于PL/0语言允许过程调用语句,且允许过程嵌套定义,因此每个过程应有过程首部以定义局部于它自己过程的常量、变量、和过程标识符,也称局部量[3]。每个过程所定义的局部量只能供它自己和它自己定义的内过程引用。对于同一层并列过程的调用关系是先定义者可以被后定义者引用,反之则不行。说明部分的处理任务就是对每个过程(包括主程序,也可看成是一个主过程)的说明对象造名字表,填写所在层次(主程序为第0层,主程序定义的过程为第1层,随着嵌套的深度增加而将层次数加大。PL/0最多允许3层)、标识符的属性和分配的相对位置等。标识符的属性不同时,所需要填的信息也不同。不同的信息是调用不同的过程完成的。所造表放在全程量一维数组TABLE表中。TX为索引表的指针,表中的每个元素为纪录型数据。LEV给出层次,DX给出每层局部量当前分配到的相对位置,可称地址指示器,每说明完一个变量后DX指示器加1。例如:一个过程的说明部分为:CONSTA=35,B=49;VARC,D,E;PRTCEDUREP;VARG对它的常量,变量和过程名说明分析后,在TABLE表中的信息如表3-1所示。表3-1TABLE表的信息NAME:ANAME:BNAME:CNAME:DNAME:ENAME:PKIND:CONSTANTKIND:CONSTANTKIND:VARIABLEKIND:VARIABLEKIND:VARIABLEKIND:PROCEDURVAL:35VAL:49LEVEL:LEVLEVEL:LEVLEVEL:LEVLEVEL:LEVADR:DXADR:DX+1ADR:DX+2ADR:NAME:G…KIND:VARIABLE…LEVEL:LEV+1…ADR:DX…在说明处理后TABLE表中的信息对于过程名的ADR域,是在过程体的目标代码生成后返填过程体的入口地址。例中在处理P过程的说明时对LEV就增加1。在P过程中的变量名的层次为LEV+1后的值。对过程还有一项数据SIZE,是记录该过程所需的数据空间。TABLE表的表头索引TX和层次单元LEV都以YUFAFENXI的参数形式出现。在主程序调用YUFAFENXI时实参值都为0.每个过程中的变量的相对起始位置在YUFAFENXI内置初值DX:=3。(2)过程体程序的主体是由语句[12]构成的。处理完过程的说明后就处理由语句组成的过程体,从语法上要对语句进行逐句分析。当语法正确时就打印程序正确。当遇到标识符的引用时就查TABLE表,看是否有过正确的定义,若已有,则从表中取相应的有关信息,供代码的生成用,若无定义则出错。4PL/0语言词法与语法分析器的设计文献[9]中指出,为了方便程序的编写,一般软件的开发都将开发工作分成若干个功能模块。本分析器的功能设计模块规划如图4-1所示。本分析器将软件中出现次数比较频繁的一些常用代码设计为独立的单元,例如设置颜色的代码和词法分析以及语法分析的代码等,这样设计新的模块时如果有重复出现的部分,只需要将编写好的模块用include语句[9]来组装就可以了。本分析器虽然分为好几个模块来设计,但是它们的功能是独立的,可以分开来设计和编写,这样有利于提高模块的内聚性[3],降低了程序的耦合性[3]。词法语法分析器词法语法分析器窗口的初始化菜单的设计工具条的设计弹出式对话框设计添加语法分析添加词法分析添加样例演示添加设置颜色图标按钮化24位位图设计关于此分析器颜色设置框窗口切割设计起始屏的设计词法语法分析图4-1分析器的设计模块图5PL/0语言词法与语法分析器的编程实现5.1编程环境简介根据开发本分析器所使用的开发工具的特点,出于安全性考虑,开发本词法与语法分析器软件时,对软件环境和硬件环境有特定的要求。5.1.1系统硬件设备本软件开发时需要具有PentiumIII处理器且满足以下要求的计算机:最低256MB内存、最小8G硬盘、鼠标、键盘。5.1.2系统支持软件本软件在WindowsXP操作系统上开发。所用的开发工具包是MicrosoftVisualStudio2005或VC++6.0。VisualC++是汇集了MS公司技术精华的主流产品,它将程序设计方法与可视的软件开发环境完美地结合在一起,其开发环境和适应Internet应用程序的特点很适合于开发Windows应用程序,因此受到广大软件设计人员的青睐。使用VisualC++6.0可以开发强大的32位应用程序,能为用户全方位地服务[1]。5.2所采用的关键技术介绍由于采用的是VisualC++6.0进行本词法与语法分析器的编写,因此根据实际需要,开发该词法与语法分析器时用到的关键技术主要有窗口初始化、切分窗口、以及位图显示技术。5.2.1窗口初始化在开发VC++应用程序时,MicroSoft公司提供了一种被称为AppWizard的动态模板生成程序[1]。创建的步骤如下:(1)建立ApplicationMicroSoftVisualC++编译器的菜单条中,选择File菜单,然后从列出的菜单项中选择“New”按钮,创建一个MFCAppWizard(exe)的应用程序,为本分析器的工程命名为“SS”,如图5-1所示。(2)生成代码及资源视图单击“OK”,选择“Singledocument”以及依次点击“Next>”直到所有设置初始化以后单击“Finish”按钮,将会看到一个对话框是AppWizard为这个项目创建的东西的简单描述,它是对各项选择的一个摘要。接着单击“OK”就可以生成如图5-2所示的关于这个窗口的代码和资源视图。图5-1工程的创建视图图5-2分析器的设计资源视图5.2.2切分窗口因为所开发的词法语法分析器既有输入程序又要求将分析结果输出在窗口中,因此为了区分输入与输出,必须实现两个窗口的并列显示,于是用到了切分窗口技术,在拆分窗口中,窗口可以被拆分成多个面板,多个面板可以相对于一个视图,也可以逐一对应视图。在窗口拆分中可分为静态窗口拆分和动态窗口拆分[1],可以用CSplitterWnd类来实现这两种类型的窗口拆分,既可以将窗口纵向拆分,也可以横向拆分。动态拆分窗口可以被拆分成多个面板,这样,当用户想同时阅读同一文档中的两个不同位置的内容时,不必打开两个窗口,而直接将该文档分成若干个窗口即可。静态拆分窗口可以被拆分成多个面板的窗口,而每个面板起到不同的作用,因此根据实际需要这里采用了静态拆分窗口技术。在主界面中嵌入CSplitterWnd成员变量时,使用CreatStatic()函数进行创建,其原型为:BOOLCreatStatic(CWnd*pParentWnd,intnRows,intnCols,DWORDdwStyle=WS_CHILD|WS_VISIBLE,UINTNid=AFX_IDW_PANE_FIRST);CWnd*pParentWnd参数表示父窗口的指针,intnRows参数表示要创建的行数,intnCols参数表示要创建的列数,DWORDdwStyle参数表示要显示的窗口风格,UINTNid=AFX_IDW_PANE_FIRST参数表示子窗口ID。5.2.3位图的显示由于该分析器的起始屏设计需要用到位图的显示技术[2],所以在制作位图显示之前,首先应该了解一下位图的显示过程:位图数据在显示之前应先转移到内存中,在WindowsAPI中,所分配的内存句柄被称为HBITMAP。它用来获得位图在内存中的地址以及长度的数据,当驻留在内存中的数据转移到视频内存中时,位图将被显示到显示器上。位图显示的实现的步骤如下:(1)创建Cbitmap对象。Cbitmap对象在首次实例化时是空的,该类所包装的位图最终必须被创建以使对象有效,格式为:CbitmapBitmap;//创建位图对象(2)加载和设置位图内容,在使用位图之前,可能希望使用有意义的数据设置位图的内容,当位图首次被创建时,内存中的数据是不可知的,所以必须在内存中添加自己的内容,这里将使用LoadBitmap()函数,格式为:Bitmap.LoadBitmap(IDR_BITMAP1);(3)将Cbitmaps绘制到屏幕上,一旦具备了一个有效位图的Cbitmap对象,就可以将它绘制到屏幕上去,在通常情况下,用BitBlt()函数,其语法如下:BOOLBitBlt(intx,inty,intnWidth,intnHeight,CDC*pSrcDC,intxSrc,intySrc,DWORDdwRop);其中参数X为位图在视窗内显示的横坐标,Y为位图在视窗内像是的纵坐标nWidth和intnHeight分别为位图的宽度和高度,pSrcDC是显示设备环境句柄,xSrc和ySrc是被显示的位图的起始位置,dwRop为位图显示形式。5.3具体功能的实现5.3.1菜单的设计与实现根据上述PL/0语言的词法与语法的描述以及词法分析和语法分析的设计思想和模块分析,首先对其菜单进行编辑和处理。如图5-3所示:利用VC++提供的组件可以轻松的对窗口的Menu组件及下拉菜单样式和内容进行编辑。其中XP用户常用组合键都自动保存在资源视图中Accelerator表中,如图5-4所示。根据需要本分析器在菜单中加入编译选项然后在该弹出式选项下拉菜单中添加词法分析、语法分析、设置颜色、样例演示四个选项,分别命名为ID_CIFA、ID_YUFA、ID_SET、ID_TEST。图5-3菜单设计视图图5-4组合键Accelerator表5.3.2窗体分割的设计与实现主窗口设置模块是本系统的界面核心部分,从某种意义上来说,真正实现了词法分析和语法分析的操作性。窗口设置如图5-5所示:5-5窗体布局设计视图本分析器采用XP菜单式样进行窗口设置,采用窗口分割技术将窗口进行了分割处理,实现窗口分割的源代码如下:BOOLCMainFrame::OnCreateClient(LPCREATESTRUCTlpcs,CCreateContext*pContext){ if(!m_wndSplitter.CreateStatic(this,1,2))//创建行列的切分窗口 { TRACE0("FailedtoCreateStaticSplitter\n"); returnFALSE; } CRectrc; //获得客户区大小 GetClientRect(rc); intx=rc.Width()/2; inty=rc.Height()/2; if(!m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CSourceEditView),CSize(x,y),pContext)) { TRACE0("Failedtocreatesecondpane\n"); returnFALSE; }//创建第个视图 if(!m_wndSplitter.CreateView(0,1, pContext->m_pNewViewClass,CSize(x,y),pContext)) { TRACE0("Failedtocreatefirstpane\n"); returnFALSE; }//创建第个视图 m_pEditSrc=&((CEditView*)m_wndSplitter.GetPane(0,0))->GetEditCtrl();//获得CEditView视图中的编辑框控件的指针 m_pEditOut=&((CEditView*)m_wndSplitter.GetPane(0,1))->GetEditCtrl();5.3.3弹出式对话框的设计与实现(1)字体和背景颜色自定义的实现为了让用户在使用该系统的时候能够方便的对所输入的程序体进行区别标识,本分析器设置了颜色自定义选项。为方便用户使用,本人将其制作成了弹出式对话框形式的取色器,如图5-6所示:图5-6颜色编辑对话框设计视图(2)关于此编译器对话框的设计在Dialog资源夹下IDD_ABOUTBOX就是VC中默认的关于此软件的对话框模板,这个对话框的作用就是显示一些关于该软件的版权信息,如图5-7所示:图5-7关于此编译器对话框设计视图5.3.4工具条的设计工具条主要用于为该分析器中菜单中的一些重要选项提供快捷方式。本软件中工具条的设计采用VC提供的Toolbar资源模板实现,工具条设计视图如图5-8所示:5-8工具条设计视图将工具条嵌入到主界面中的关键代码如下:intCMainFrame::OnCreate(LPCREATESTRUCTlpCreateStruct){ if(CFrameWnd::OnCreate(lpCreateStruct)==-1) return-1; if(!m_wndToolBar.CreateEx(this,TBSTYLE_FLAT,WS_CHILD|WS_VISIBLE|CBRS_TOP |CBRS_GRIPPER|CBRS_TOOLTIPS|CBRS_FLYBY|CBRS_SIZE_DYNAMIC)|| !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) { TRACE0("Failedtocreatetoolbar\n"); return-1;//failtocreate }m_wndToolBar.SetButtonText(0,"新建");//你所需要做的工作只需要添加这些:)m_wndToolBar.SetButtonText(1,"打开");//第一个参数是图标索引,第个参数是想要显示的文本m_wndToolBar.SetButtonText(2,"保存");m_wndToolBar.SetButtonText(4,"剪切");m_wndToolBar.SetButtonText(5,"复制");m_wndToolBar.SetButtonText(6,"粘贴");m_wndToolBar.SetButtonText(8,"打印");m_wndToolBar.SetButtonText(10,"关于");m_wndToolBar.SetButtonText(11,"词法分析");m_wndToolBar.SetButtonText(13,"语法分析");m_wndToolBar.SetButtonText(14,"设置颜色");m_wndToolBar.SetButtonText(15,"样例演示");m_wndToolBar.SetButtonText(16,"退出");m_wndToolBar.SetSizes(CSize(90,45),CSize(24,24));5.3.5起始屏的设计到此为止,本词法语法分析器的几个主要功能模块已经编码实现了,已经可以作为一个单独的小软件来进行程序测试了。接下来的任务就是美化软件的界面,使之趋于完善。通常在启动一个大型应用程序时,往往要等待相当长的一段时间(从几秒到几十秒),在这段时间里,如果使用起始屏,会产生相当好的效果。起始屏一般被设置为一幅位图,其处理过程是:先在内存中加载一幅位图,然后启动一个定时器,当定时结束时,关闭位图显示。在VisualC++中,起始屏的设计是可以通过组件来完成的。只需在Project菜单下选择AddtoProject选项下的Components&Controls,选择VisualC++Components中的SplashScreen组件即可,实现的代码如下:BOOLCLogoThread::InitInstance(){//说明:通常,系统内的每个线程都有自己的输入队列。本函数(既“连接线程输入函数”)允许线程和进程共享输入队列。连接了线程后,输入焦点、窗口激活、鼠标捕获、键盘状态以及输入队列状态都会进入共享状态::AttachThreadInput(m_nThreadID, //欲连接线程的标识符(ID) AfxGetApp()->m_nThreadID, //与idAttach线程连接的另一个线程的标识符 true ); //TRUE(非零)连接,FALSE撤消连接m_pLogoDlg=newCLogoDlg;m_pLogoDlg->Create(IDD_LOGODLG);m_pLogoDlg->ShowWindow(SW_SHOW);//在这个用户界面线程中创建对话框returntrue;}voidCLogoThread::HideSplash(){ m_pLogoDlg->SendMessage(WM_CLOSE);}intCLogoThread::ExitInstance(){ m_pLogoDlg->DestroyWindow(); deletem_pLogoDlg; returnCWinThread::ExitInstance();}BEGIN_MESSAGE_MAP(CLogoThread,CWinThread)END_MESSAGE_MAP()使用SplashScreen组件时,实现起始屏的设计的代码是在logo.cpp中编辑的,而起始屏的图片设计代码则在logodlg.cpp中实现。图5-9为起始屏的设计视图:图5-9起始屏的设计视图5.4PL/0语言词法与语法分析器的测试5.4.1测试的重要性软件开发过程可分为:需求分析、软件设计、软件实现和软件测试四个阶段。需求分析的结果决定输入,测试后的软件是最终的输出。重视软件的输入和输出,就把住了产品的质量关。成熟的客户一定会积极参与测试,测试不到家时是绝对不会允许软件投产的;成熟的软件项目开发团体都会安排充足的时间对系统进行测试。一般来说越早发现软件问题,改正的成本越低,破坏性越小。所以,在系统发布前,要尽量多地把软件可能存在的问题找出来。找问题的主要手段就是有计划、有组织地进行充分的测试。如果不测试,等软件投产后发现的问题,其危害性将被成倍放大,将直接损坏开发商和客户双方的利益和声誉。总之,是否重视所开发软件的测试[3],是判定开发商和客户是否成熟的重要标志。5.4.2分析器的测试过程和运行效果本软件的设计思路简单,因此不需要特别多的测试环节。本次毕业设计只设计了一个样例程序演示词法分析与语法分析的运行情况。点击工具条上的“演示”按钮,得到如图5-10的结果,测试程序按设计要求输出在左边的输入窗口中:图5-10点击样例演示效果图点击“词法分析”按钮,分析器对测试程序进行词法分析的结果按要求输出在图5-11所示的右边输出窗口中:图5-11测试程序词法分析结果点击“语法分析”按钮,分析器对测试程序进行语法分析的结果按要求输出在图5-12所示的右边输出窗口中:图5-12测试程序的语法分析结果单击“关于”按钮,显示如图5-13所示的版权信息:图5-13关于此编译器单击“颜色”按钮,运行效果如图5-14所示,可设置背景、前景颜色效果。图5-14颜色设置效果图6结束语通过这次毕业设计,实现了PL/0语言的词法语法分析器的大部分功能,让本人对VC++语言有了一个比较深入的了解,而且对于编程语言和编程工具有了一个深刻的认识,掌握了一些软件的基本操作,以及软件界面的设计,熟悉了一些简单的程序设计过程和界面美化方法。虽然该PL/0词法与语法分析器大部分功能已经实现了,但其中可能仍存在许多不足之处。首先,系统的界面的设计还不够美观,技巧性不强,布局不很合理;其次,这个词法语法分析器的性能如何还没有经过实际应用的大量测试和考验,譬如,分析器仅限于对PL/0语言的程序进行词法和语法分析,具有很大的局限性,这是一个比较现实的问题;另外在实际的运行中可能还会出现隐藏的问题。而且由于时间仓促,作者水平有限,没有对系统其他一些十分有用的功能进行发掘和实现,还有许多地方尚待改进和完善。参考文献[1]费雷泽.可变目标C编译器设计与实现[M].北京:电子工业出版社,2005[2]CharlesN.Fischer.编译器构造C语言设计[M].北京:机械工业出版社,2005[3]吕映芝,张素琴,蒋维杜.编译原理[M].北京:清华大学出版社,2005[4]AndrewW.Appel.现代编译原理C语言描述[M].北京:人民邮电出版社,2006[5]陈火旺,钱家骅,孙永强.程序设计语言编译原理[M].北京:国防工业出版社,1980[6]KennethC.Louden.编译原理及实践[M].北京:机械工业出版社,2000[7]鸿健.C语言高级程序员编程指南[M].北京:中科院希望出版社,2000[8]刘海涛.TurboPascal程序设计基础[M].北京:清华大学出版社,2000[9]斯科特.程序设计语言—实践之路[M].北京:电子工业出版社,2005[10]AndrewKoenig.C陷阱与缺陷[M].北京:人民邮电出版社,2002[11]温敬和.编译原理使用教程[M].北京:清华大学出版社,2005[12]阿霍,塞西,厄尔曼.编译原理技术与工具[M].北京:人民邮电出版社,2002附录词法语法分析程序头文件fenxi.h:/*************************************************************************文件名:FenXi.h*文件描述:词法语法分析的头文件*创建人:黄远强,2007年04月15日*版本号:1.0************************************************************************/#if!defined_FENXI_H#define_FENXI_HstructCIFA //保存词法分析结果{ intnType; //0:错误, 1:标志符, 2:数字, 3-:关键字和操作符, -1:结束符 intnValue; //二元式中的值 charszText[20]; //单词 charszDesc[50];//描述 intnAddr; //源文件缓冲区中地址};classCFenXi{ public: voidYuFaFenXi();//语法分析 voidCiFaFenXi();//词法分析 intm_nErrAddr; //语法错误对应单词的地址 intm_nErrNo; //语法错误代码 charm_str[20000]; //源程序缓冲区 charm_szErrMsg[100][100]; //错误信息表 CIFA*m_cifa[10000]; //词法分析结果protected: voidinit(); intm_nCur; //用于语法分析中,指示词法分析结果表中当前的位置 intm_n; //用于词法分析中,用于指示词法分析结果的个数 charm_kw[50][20]; //关键词表 intFinKW(char*);/*语法分析函数*/ boolChengXu(); //程序 boolChengXuTi(); //程序体 boolShengMing(); //声明 boolShengMingChuan(); //声明串 boolBianliangShengMing(); //变量声明 boolChangliangShengMing(); //常量声明 boolGuoChengShengMing(); //过程声明 boolShiCan(); //实参 boolXingCan(); //形参 boolYuJu(); //语句 boolYuJuChuan(); //语句串 boolGuoChengYuJu(); //过程语句 boolXunHuanYuJu(); //循环语句 boolTiaoJianYuJu(); //条件语句 boolFuZhiYuJu(); //赋值语句 boolFuHeYuJu(); //复合语句 boolBiaoDaShi(); //表达式 boolGuanXiBiaoDaShi(); //关系表达式 boolBoolBiaoDaShi(); //布尔表达式 boolXiang(); //项 boolYinZi(); //因子 boolGuanXi(); //关系/**/};#endif词法语法分析程序fenxi.cpp:/*************************************************************************文件名:FenXi.cpp*文件描述:词法语法分析的实现文件*创建人:黄远强,2007年04月15日************************************************************************/#include"stdafx.h"#include"fenxi.h"/*================================================================*函数名:CiFaFenXi*功能描述:词法分析(public)*返回值:void================================================================*/voidCFenXi::CiFaFenXi(){ BOOLflag=false; chartoken[20]; intk,v; init(); for(inti=0;i<m_n;i++)//当第2次调用该函数时,就要释放前1次的资源 { deletem_cifa[i]; } intn=0; //用于指示当前的字符 m_n=0; //词法结果单词的个数 while(m_str[n]) { if(flag) { while(!((m_str[n]=='*')&&(m_str[n+1]=='/'))) { if(m_str[n]) n++; else break; } if(m_str[n]) { n++; n++; flag=false; } } while(1) { while((m_str[n]==32)||(m_str[n]==9)) n++; if(!((m_str[n]==13)&&(m_str[n+1]==10))) break; n++; n++; } if(isalpha(m_str[n])) //字母 { k=0; while(1) { if(k<19) //标志符的长度为20 token[k++]=m_str[n++]; else n++; if(!isalnum(m_str[n]))//如果不是数字或字母就退出 break; } token[k]=0; v=FinKW(token); //查找关键词表 if(v) //如果是关键词 { m_cifa[m_n]=newCIFA; m_cifa[m_n]->nValue=0; m_cifa[m_n]->nType=v; strcpy(m_cifa[m_n]->szText,token); strcpy(m_cifa[m_n]->szDesc,"基本字"); m_cifa[m_n]->nAddr=n-k; m_n++; } else//普通标志符 { m_cifa[m_n]=newCIFA; /*在词法分析结果中查找*/ intvv=1; for(inti=0;i<m_n;i++) { if(m_cifa[i]->nType==1) { vv++; if(!::stricmp(m_cifa[i]->szText,token)) m_cifa[m_n]->nValue=m_cifa[i]->nValue; } } m_cifa[m_n]->nValue=vv; /**/ m_cifa[m_n]->nType=1; strcpy(m_cifa[m_n]->szText,token); strcpy(m_cifa[m_n]->szDesc,"标识符"); m_cifa[m_n]->nAddr=n-k; m_n++; } } elseif(isdigit(m_str[n])) //数字 { k=0; BOOLerror=false; while(1) { if(k<=8) token[k++]=m_str[n++]; else { error=true; n++; } if(!isdigit(m_str[n])) break; } token[k]=0; v=::strtol(token,NULL,10); if(error) { m_cifa[m_n]=newCIFA; m_cifa[m_n]->nValue=1; m_cifa[m_n]->nType=0; strcpy(m_cifa[m_n]->szText,token); strcpy(m_cifa[m_n]->szDesc,""); m_cifa[m_n]->nAddr=n-k; m_n++; } else { m_cifa[m_n]=newCIFA; m_cifa[m_n]->nValue=v; m_cifa[m_n]->nType=2; strcpy(m_cifa[m_n]->szText,token); strcpy(m_cifa[m_n]->szDesc,"常数"); m_cifa[m_n]->nAddr=n-k; m_n++; } } else switch(m_str[n]) //其他符号 { case'+': case'-': case'*': case'=': token[0]=m_str[n]; token[1]=0; v=FinKW(token); m_cifa[m_n]=newCIFA; m_cifa[m_n]->nValue=0; m_cifa[m_n]->nType=v; strcpy(m_cifa[m_n]->szText,token); strcpy(m_cifa[m_n]->szDesc,"运算符"); m_cifa[m_n]->nAddr=n; m_n++; n++; break; case'~': case'&': case'|': case';': case'.': case',': case'(': case')': token[0]=m_str[n]; token[1]=0; v=FinKW(token); m_cifa[m_n]=newCIFA; m_cifa[m_n]->nValue=0; m_cifa[m_n]->nType=v; strcpy(m_cifa[m_n]->szText,token); strcpy(m_cifa[m_n]->szDesc,"界符"); m_cifa[m_n]->nAddr=n; m_n++; n++; break; case'\0': break; case'/': switch(m_str[n+1]) { case'*': n++; n++; flag=true; break; default: token[0]=m_str[n]; token[1]=0; v=FinKW(token); m_cifa[m_n]=newCIFA; m_cifa[m_n]->nValue=0; m_cifa[m_n]->nType=v; strcpy(m_cifa[m_n]->szText,token); strcpy(m_cifa[m_n]->szDesc,"___"); m_cifa[m_n]->nAddr=n; m_n++; n++; } break; case'<': switch(m_str[n+1]) { case'=': token[0]=m_str[n]; token[1]=m_str[n+1]; token[2]=0; v=FinKW(token); m_cifa[m_n]=newCIFA; m_cifa[m_n]->nValue=0; m_cifa[m_n]->nType=v; strcpy(m_cifa[m_n]->szText,token); strcpy(m_cifa[m_n]->szDesc,"运算符"); m_cifa[m_n]->nAddr=n; m_n++; n++; n++; break; case'>': token[0]=m_str[n]; token[1]=m_str[n+1]; token[2]=0; v=FinKW(token); m_cifa[m_n]=newCIFA; m_cifa[m_n]->nValue=0; m_cifa[m_n]->nType=v; strcpy(m_cifa[m_n]->szText,token); strcpy(m_cifa[m_n]->szDesc,"运算符"); m_cifa[m_n]->nAddr=n; m_n++; n++; n++; break; default: token[0]=m_str[n]; token[1]=0; v=FinKW(token); m_cifa[m_n]=newCIFA; m_cifa[m_n]->nValue=0; m_cifa[m_n]->nType=v; strcpy(m_cifa[m_n]->szText,token); strcpy(m_cifa[m_n]->szDesc,"运算符"); m_cifa[m_n]->nAddr=n; m_n++; n++; } break; case'>': switch(m_str[n+1]) { case'=': token[0]=m_str[n]; token[1]=m_str[n+1]; token[2]=0; v=FinKW(token); m_cifa[m_n]=newCIFA; m_cifa[m_n]->nValue=0; m_cifa[m_n]->nType=v; strcpy(m_cifa[m_n]->szText,token); strcpy(m_cifa[m_n]->szDesc,"运算符"); m_cifa[m_n]->nAddr=n; m_n++; n++; n++; break; default: token[0]=m_str[n]; token[1]=0; v=FinKW(token); m_cifa[m_n]=newCIFA; m_cifa[m_n]->nValue=0; m_cifa[m_n]->nType=v; strcpy(m_cifa[m_n]->szText,token); strcpy(m_cifa[m_n]->szDesc,"运算符"); m_cifa[m_n]->nAddr=n; m_n++; n++; } break; case':': switch(m_str[n+1]) { case'=': token[0]=m_str[n]; token[1]=m_str[n+1]; token[2]=0; v=FinKW(token); m_cifa[m_n]=newCIFA; m_cifa[m_n]->nValue=0; m_cifa[m_n]->nType=v; strcpy(m_cifa[m_n]->szText,token); strcpy(m_cifa[m_n]->szDesc,"运算符"); m_cifa[m_n]->nAddr=n; m_n++; n++; n++; break; default: token[0]=m_str[n]; token[1]=0; v=FinKW(token); m_cifa[m_n]=newCIFA; m_cifa[m_n]->nValue=0; m_cifa[m_n]->nType=v; strcpy(m_cifa[m_n]->szText,token); strcpy(m_cifa[m_n]->szDesc,"界符"); m_cifa[m_n]->nAddr=n; m_n++; n++; } break; default: token[0]=m_str[n]; token[1]=0; m_cifa[m_n]=newCIFA; m_cifa[m_n]->nValue=2; m_cifa[m_n]->nType=0; strcpy(m_cifa[m_n]->szText,token); strcpy(m_cifa[m_n]->szDesc,"___"); m_cifa[m_n]->nAddr=n; m_n++; n++; } if(m_n==10000-2) //词法分析的结果的个数规定为10000 { m_cifa[m_n]=newCIFA; m_cifa[m_n]->nValue=99; m_cifa[m_n]->nType=0; strcpy(m_cifa[m_n]->szText,""); strcpy(m_cifa[m_n]->szDesc,"错误"); m_cifa[m_n]->nAddr=n-1; m_n++; break; } } m_cifa[m_n]=newCIFA; m_cifa[m_n]->nValue=0; m_cifa[m_n]->nType=-1; //结束符 strcpy(m_cifa[m_n]->szText,""); strcpy(m_cifa[m_n]->szDesc,"程序结束"); m_cifa[m_n]->nAddr=0; m_n++; return;}/*================================================================*函数名:FinKW(char*a)*功能描述:在关键字表中查找(protected)*返回值:int(如果找到返回在表中的位置,否则返回0)================================================================*/intCFenXi::FinKW(char*a){ for(inti=0;i<50;i++) if(!::stricmp(m_kw[i],a))//找到 returni; return0;//未找到}/*================================================================*函数名:YuFaFenXi*功能描述:语法分析(public)*返回值:void*作者:黄远强2007年4月15日================================================================*/voidCFenXi::YuFaFenXi(){ if(m_n==0) return; //尚未进行词法分析 m_nCur=0; //m_nCur用语指示词法分析结果表中单词的位置 ChengXu(); //从程序开始 return;}/*================================================================*函数名:ChengXu*功能描述:分析整个程序(protected)*返回值:bool*示例: Programabc; 这里是程序体 .================================================================*/boolCFenXi::ChengXu() //程序{ if(m_cifa[m_nCur]->nType!=3) { m_nErrNo=3; //缺少程序声明符号program m_nErrAddr=m_nCur; returnfalse; } m_nCur++; //分析下一个单词 if(m_cifa[m_nCur]->nType!=1)//标识符 { m_nErrNo=4; //program后缺少标识符! m_nErrAddr=m_nCur; returnfalse; } m_nCur++; if(m_cifa[m_nCur]->nType!=30)//; { m_nErrNo=5; //程序声明后缺少; m_nErrAddr=m_nCur; returnfalse; } m_nCur++; if(!ChengXuTi())//分析程序体 returnfalse; if(m_cifa[m_nCur]->nType!=31) //.(程序的最后一个符号) { m_nErrNo=6; //缺少程序结束符. m_nErrAddr=m_nCur; returnfalse; } m_nCur++; if(m_cifa[m_nCur]->nType!=-1) { m_nErrNo=96; //源程序结束符.后还有多余的内容! m_nErrAddr=m_nCur; returnfalse; } m_nErrNo=0; //语法分析成功 m_nErrAddr=m_nCur; returntrue;}/*================================================================*函数名:ChengXuTi*功能描述:分析程序体(protected)*返回值:bool*示例: varx,y,z;//变量声明 consta,b;//常量声明 Procedureab(Integerm,n:var;t:const);//过程声明 Begin t:=n+m; Ifm>nthenn:=telsem:=t; Whilen<mdo Begin n:=n+1 End End; Begin 这里是语句串 End================================================================*/boolCFenXi::ChengXuTi(){ switch(m_cifa[m_nCur]->nType) { case5: //procedure case14: //var case15: //const if(!ShengMingChuan())//声明串(用;隔开的多个变量或过程声明) returnfalse; if(m_cifa[m_nCur]->nType!=30)//; { m_nErrNo=5; //缺少;!" m_nErrAddr=m_nCur; returnfalse; } m_nCur++; break; case6: /

温馨提示

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

评论

0/150

提交评论