PL0 编译系统及集成开发环境的实现文档.docx
P1.O编译系统及集成开发环境的实现报告北航计算机学院32060624王克Contents:1项目HU21.1 实现折充的P1.O编译内核说明21.2 P1.o控制台程Jf简介21.3 P1.O集成开发环境P1.ODE简介22扩充P1.o诺言文法32.1 诺法的EBNF(巴克斯瑙尔范式)32.2 诺法图43属性H译文法(包括各动作符号及各种房住的说明)64P-COde代码指令说明85措课信息表96系缆的设计与实,IO6.1 (Plo类)P1.0编译程序的结构(参考教材原版P1./O)106.2 P1.O文法犷充(else和for)Sit和实现116.3 控制台P1.o编译系统结构图(DebUgAndon类,Sym类,PIO类)136.4 P1.O集成开发环境P1.ODE实现156.5 源代周文件说明157系统工作过程说明167.1 (PIO类)培法分所过程(参考教材原版P1./0)167.2 (Sym类中)P1.0编译程If的司法分析167.3 P1.O集成开发环境工作流程188源程序和制箕用例188.1 源程序188.2 系统测试189开发体会。得199.1 编译学习体会199.2 项目实践心得199.3 开发日志1910附录2010.1 参考资料201项目柢述在学玩编译原理这门课程后,一方面由于大作业的要求,另一方面渴望所学知识得到实践,便有了开发一个简单的P1.O集成开发环境的设想。本项目的开发过程可以分成三个阶段,即实现并扩充的P1.O编译内核,P1.o控制台程序开发,P1.O集成开发环境P1.ODE开发。其中主要任务是集中在第一阶段,后两阶段只是在其基础上实现一个对外接口,并对其进行改进。1.1 实现扩充的P1.O编译内核说明在这一阶段,主要是深入了解P1.O编译器内核的构造方法,边阅读由著名计算机科学家Pascal语言的创始人N.Wirth的P1.O编译系统,同时用C+语言进行重写。这其中涉及到许多PaSCal语言和C+的语言设施不同,重新设计以实现。主要在Win2003÷VC6.0环境下完成。1.2 P1.o控制台程序简介在己有内核的基础上,充分发挥C+语言的特性,及一点点面向对象的思想,重新设计了Pu)编译系统的架构。同时对原有P1.o文法进行一定的扩充,实现控制台,文件双重输出,又设计了调试信息输出。此阶段主要在Win2003+VC7.l环境下完成。1.3 P1.o集成开发环境P1.oDE简介在扩充P1.O系统的基础上及以前开发数据库系统的经验,实现一个P1.O编译程序的IDE,最终成为P1.0集成开发环境P1.0DE(P1.0DevelopmentEnvironment)。主要在Win2003+BCB6.0环境下完成。1.4 项目成果展示1.4.1.1P1.O集成开发环境P1.ODEP1.Olfcjft开发"MVO8(P1.ODovelopwontEnvironment)PovorcdbyITRunner(320606243E)12005.3.3OMIC<-piiIEaiqlJfip打开PCODEftW符号筏consttrue=I,fals=0.var×>y»a>tpf:intccer.procedureprine;verI9f:integer.procedureod.x:«x-x/y«y:beginf三三true:iz3.vhilei<>dobeginx;e»:yx三i.盥翁榜三三z的熊O1-3456-S9Eol>-5456-S91!lllll!llIWI>-getffy>O.<-condition();一expression。.I<-tr>().II<-factorO;III0;Illl>-trt();III<-postion().欢迎2使用rsD*3h:200S->414.17:231.4.1.2P1.O控制台编译系统2扩充P1.O语言文法2.1 晤法的EBNF(巴克斯-瑙尔范式)程序:=分程序. 无符号整数 标识符 变量说明部分 过程说明部分 过程首部 语句赋值语句表达式项因子加法运算符乘法运算符条件语句关系运算符条件语句 分程序:=常量说明部分变量说明部分过程说明部分语句 常量说明部分:=const常量定义,常量定义; 常量定义:=标识符=无符号整数:=数字数字):=字母字母|数字var标识符,标识符;过程首部分程序;过程说明部分;ProCedUre标识符;赋值语句|条件语句|当循环语句|过程调用语句|复合语句|读语句|写语句|空标识符:=表达式:=+卜项加法运算符项:=因子乘法运算符因子标识符|无符号整数'C表达式):=+-:=*|/IF条件THEN语句if条件then语句elsev语句 当型循环语句:=WhiIe条件do语句 VfOr循环语句:=forv表达式(todownto)v表达式byv表达式dov语句 过程调用语句:=call标识符 复合语句 读语句 写语句 字母 数字:=begin语句;语句end:=read'('标识符,标识符')':=WriteT表达式,表达式')':=abcd.xyz:=0l23.892.2 暗法图分程今语法图(扩充)2005年3月311const)idem)Ynumber)-procedure)Yidem)“分程32060624T克OI语句IV语句>语法图(扩充)2005年33H32060624卜克V条件>语法图2005年3川3口32060624卜克25<l3JJ3II32060624I克V项>语法图2005年3月31132060624I克V因子>语法图2005<3JJ3H32060624上克-因子(Ident)umbcrI9T表达式I>-3属性Sl译文沫(包括各动作符号及各种属性的说明)无符号整数v-数字“V数字mvalvn,m,v:数字的值val由数字获得整数的值标识符“::=V字母字母K数字公/n:标识符的名字bsf:获得标识符的名字常量定义:=标识符11=无符号整数enterncn:常量名c:无符号整数的值enter:把常量名和无符号整数的值登如符号表变量说明部分:=var标识符11entern,标识符n:变量名enter:把变量名登入符号表过程首部::=ProCedUre过程名,©enternn:过程名enter:把过程名登入到符号表赋值语句:=Sei1.l标认符:=reset1.1.表达式1.:左值特征set1.:置左值特征为真reset1.:置左值特征为假表达式:=+-fu项+V项add-V项subfu:发送取反指令add:发送加法指令sub:发送减法指令项:=V因子*mul因子因子mul:发送乘法指令div:发送除法指令因子:=V标识符positionnpushjK无符号整数pushii,(,表达式,),n:常量名或者变量名j:名字n在符号表中的位置i:整数©position:查询j在符号表中的位置push:发送1.OD指令pushi发送1.IT指令条件:=表达式vV表达式neq表达式表达式1SSI表达式V=V表达式lesI表达式表达式gtrI表达式=V表达式geqIodd表达式oddneq:发送不等于判断指令lss:发送小于判断指令les:发送小于等于指令gtr:发送大于判断指令geq:发送大于等于判断指令odd:发送不等于判断指令条件语句:=if条件brf丫then语句labprod丫brf:发送条件跳转指令labprod:把继承属性y回填到目标程序中当循环:二(while头fr语句©retbranchrIabmit.r(while头fl:=Whilelabgen条件>false_branchdof,r:转移位置ret_branch:发送JMP指令labmit:发送JPC指令labgen:返回循环结束位置false_branch:产生位置标号< 过程调用语句>:=call<标识符>11100kupprocnigenjsrin:过程名i:过程名在符号表中的位置loOkUPProc:返回过程名在符号表中的位置genjsr:压栈< 读语句>:=read,(,<标识符>redn><标识符>red11,),red发送RED读指令< 写语句>:=write,(,<表达式>jwrtj><表达式>jwrtj,),j:输出值Wrt:发送WRT输出指令4P-Code代码指令说明P1./O编译程序所产生的目标代码是一个假想栈式计算机的目标代码即P-code指令代码。P-COde不依赖于任何实际计算机,其指令格式如下:f1a其中f为功能码;1表示层次差,即变量或过程被引用的分程序与说明该变量或过程的分程序之间的层次差;a的含义对不同的指令有所区别,可以是常数值、位移量、操作符代码等。目标指令有10条:P-code指令1.说明A说明指令含义11.IT0aa为常数取常量H放到数据栈顶2OPRIa1为层差a为表示执行何种运算执行运算(详见代码中interpret)31.OD1a1为调用层与说明层的层差a为变量在所说明层中的相对位置取变量放到数据栈栈顶4STO1a1为调用层与说明层的层差a为变量在所说明层中的相对位置将数据栈顶内容存入变量5CA1.1a1为层差a为被调用过程的目标程序入口地址调用过程6INT0aa为开辟的单元个数数据栈栈顶指针加a7JMP0aa为转向地址无条件转移到a8JPC0aa为转向地址条件转移9RED1a1为层次差a为相对地址读数据并存入变量()10WRT00-将栈顶内容输出5错误信息表本程序具有输出编译错误编号及错误信息的能力,由于对P1.O进行了扩展,所以错误类型也相应增加了5种。"error01:应为“二”而不是“:二"”,,error02:“二"后应为数字”,',error03:标识符后应是“二"”,"error04:const,var,procedure后应为标识符","error05:漏掉或或"error06:过程说明后的符号不正确(应是语句开始符或过程开始符)“,,error07:应是语句","error08:程序体内语句部分的符号不正确”,,error09:应为句号、'",,error10:语句之间漏了',errorll:标识符未说明”,"error12:不可向常量或过程赋值”,"error13:应是赋值号,error14:call后应为标识符”,"error15:不可调用常量或变量","error16:应为then","error17:应为或end”,,error18:应为do”,,error19:语句后的标识符不正确”,"error20:应为关系运算符”,"error21:表达式内不可有过程标识符”,,error22:漏掉右括号"error23:因子后不可为此符号”,"error24:表达式开始符不能是此符号","error25:",error26:“,,error27:","error28:","error29:“,,error30:这个数太大”,"error31:",/*扩充部分错误*/,error32:"error33:"error34:',error35:',error36:分程序层次太深”,缺少注释大特号不匹配北没有指定变量类型“,缺少to或down",for语句中的to或downto的范围错误","error37:“,,error38:“,,error39:“,"error40:应为左括号”6系统的投计与实现6.1 (PIO类)P1.O编译程序的结构(参考教材原版P1./O)P1./0语言可以看成PASCA1.语言的子集,它的编译程序是一个编译解释执行系统。P1./0的目标程序为假想栈式计算机的汇编语言,与具体计算机无关。其编译过程采用一趟扫描方式,以语法分析程序为核心,词法分析和代码生成程序都作为一个独立的过程,当语法分析需要读单词时就调用词法分析程序,而当语法分析正确需要生成相应的目标代码时,则调用代码生成程序。用表格管理程序建立变量、常量和过程表示符的说明与引用之间的信息联系。用出错处理程序对词法和语法分析遇到的错误给出在源程序中出错的位置和错位性质。当源程序编译正确时,P1./0编译程序自动调用解释执行程序,对目标代码进行解释执行,并按用户程序的要求输入数据和输出运行结果。P1./U源程序表格管理程序目标程序出错处理程序P1./D语言目标程序P1./D语言解催执行程序内的中文表示子程序或过程>表示数据流A表示调用关系原版的P1./O语言使用PASCA1.语言书写的,整个编译程序(包含主程序)是由18个嵌套并列的过程或函数组成。函数功能说明表:过程或函数名功能简要说明pl主程序error出错处理,打印出错位置和错误编码getsym词法分析,读取一个单词getch漏掉空格,读取一个字符gen生成目标代码,并送入目标程序区test测试当前单词符是否合法block分析程序处理过程enter登录名字表PoSitiOn(函数)查找标识符在名字表中的位置constdeclaration常量定义处理Vardeclaration变量定义处理listcode列出目标代码清单statement语句部分处理expression表达式处理term项处理factor因子处理condition条件处理interpret对目标代码的解释执行程序base(函数)通过静态链求出数据区的基地址过程或函数的嵌套定义结构p10errorgetsymgetchgentestblockenterPOsitiOn(函数)ConstdeclarationVardeclarationlistcodestatementinterpretbase(函数)6.2 P1.O文法犷充(else6for)设6和实现6.2.1 Ifthen(else)实现条件语句ifv条件Xhenv语句elseohcr语句exij读入if;处理表达式(if.»调用Condition) JPCO,other(不满足条件跳Other,Other先设为0,后面填写) 读入then;处理语句(then,调用Statement) JMPO,exit(完毕跳过else,exit先设为0,后面填写) 读入else 设置标记other(这里就是不满足条件应当执行的地方) 处理语句>(else.>调用Statement) 设置标记exit(这里就是执行完then以后接下来执行的地方)6.2.2For循环的实现VfOr循环语句>:=forV表达式1>(todownto)oopV表达式2>byV表J式3>dstart<U>endloop 读入for;处理V表达式1>JMPO,start:(执行循环) 读入todownto 设置标记loop(循环体的开始) 处理V表达式2>(读入循环截止常量) 读入by;1.ODl,id(取出id) 处理V表达式3>(读入循环step) OPR0,2;STOl,id(id加step,如果SteP可为负数) 根据to/downto设置不同的条件判断处理(OPR0,11或OPR0,13) JPC0,endloop(完成循环次数,退出循环) 读入do 设置标记Start(回填Start) 处理语句>(执行do部分) JMPOJoop(循环) 设置标记endloop(回填endloop的代码地址)File:PIOxpp6.3控制台P1.o编译系统结构图(DebugAndOn类,Sym类,PIO类)类图如下:ODebugAiidOutQAttrioute; counter oQPlOAttriutjrodlndax-dcbvtfin-INsldOft1.C-EnUEoni:outOwethod三OOFlZha"fi'l杷me)-RUrO:-o-St3rtCo-npiJodbssei-I-esitvsl5re.:niI.<-b;O:<"sy11-*三?:.si11tt¢("<11!.OMconcitonix.rrctf.s.intt35lndQM.ntlc-o:d-conrtCe5-3tontG,<rt6ucotslde>intSvkv1e。-<1idIM&fadQrCr1。,MAlrt三tTn4<aI*%,j>:iG-t,a*t&t3=llHda.11t5dataln=axrtSla.).ccexpresso>>n-*setir>sinttoolelndex,.11eIesodfacl(s-imistrav3irttablel11dexi11t«,;0igon(fctxntr,nt2).oc-got:/F.0切:ClCinterprelJ;DiUTISyCdg?ntcodTndexD):.<.-positione>sr*drttabldndcx»int三tatenentfrmsetsvs.inttabklndexintei1oid-trnvm÷t«,”.11t÷t÷TdexIH.:-test(smswt:1三zr三t2-tnj:ci<va*Zeleraini-iIabIeIndexint&=ataace×/nt&I竺、od-WO'OTableDAttbute三-3dorkind一lvei-namoOinSirUCtiOnOtt*but«-adcrfine-IJobjectrorCount 3trirGOvthoccOOrCEBUGQtateem&detugti11t-11to,chrW-11x,chc-,.o.三-CEBCG:of式TWm&debugvt.Intnto,chr">sInt1.:ntJ):volj Cogndut:a力 OUTPUTo*三*eam&cut.chaxrrg»1JCiU lp<j-r'.>cu:char-m*jt。" 1.TTPu*:。*-1.cur.ca*msgl,c*rr«g;Ct1.E:CZ>:QywfcDebugAncCut(oicSymOAUibutes- Cb- chaCcuM-Id- IiwEFi- Iic1.oC- IinMUF- number- rclhu-n-3QUr-=eEndF三ym- tocnVQrSOvec<J5OOGecSyrrboIi三t-e3rr>&n11.OFtreaF=ut,三rrt-ea>&d三feu三Cvt;JCiU"Srm"-gster(tra-nS><rctr三m1.otcx三t-e0*cg-ut:“arget三>rr,jstran2nCYtrUaF&ovt-oe三11&de=ugut':oid3m(vccOEY-卜P=CymbOi6.4P1.o集成开发环境P1.ODE实现6.4.1 核心编译部分说明使用控制台P1.o的基本代码,实现快速开发。6.4.2 界面部分说明使用RichEdit来显示文本信息:源文件(用户打开),output.txt文件(生成),debug.txt文件(生成)使用StringGrid来实时显示在编译过程三个表的内容:栈式符号表,P-Code代码表,栈式计算机运行栈使用Button来控制编译和运行。使用CheCkBOX来设置是否需要单步编译,单步运行。注:源代码编辑框中虽具有编辑功能,但不会对文件造成影响。(有待扩展)6.4.3 用户手册详见P1.OWin32下的Manua1.chme6.5源代码文件说明文件名说明1P10_Win32_VC71.vcprojVisualC+7.1项目文件2P10_Win32_VC71.sln解决方案文件3P10_Win32_VC71.suo解决方案选项文件4PI0-Win32-VC7l.ncb非编译浏览器文件5DebugAndOutxpp实现文件,提供编译输出信息接口。6DebugAndOut.hDebugAndOut类的头文件7Sym.cpp单词类,可为PIO类提供词法分析。基类为DebUgAndOUt8Sym.hSym类的头文件9P10.cppPK)类实现文件,为编译运行PK)源文件提供接口。基类为DebugAndOut10P10.hPK)类的头文件11PlOlest-Cpp定义控制台应用程序的入口点。6.5.1P1.O集成开发环境P1.ODE文件名说明1P10.bprBCB项目文件2main.dfm主窗口的窗体文件3FrmInputJfm输入数据窗口的窗体文件4output.dfm运算结果显示窗口的窗体文件5P10.cpp定义应用程序的入口点6main.cpp主窗口类的实现文件(包含P1.O编译器的三个类)7main.h主窗口类的的头文件8FrmInputxpp数据输入类的实现文件9Frmlnput.h主窗口类的的头文件10output.cpp运算结果显示窗口类的实现文件11output.h运算结果显示窗口类的的头文件7系统工作过程说明7.1 (PlO类)语法分析过程(参考教材原版P1./O)语法分析过程B1.OCK是整个编译过程的核心。编译程序的总体流程如下图:(三)7.2 (Sym类中)P1.O编译程序的司法分析P1./0的词法分析程序GETSYM是一个独立的过程,其基本功能是为语法分析提供单词,是语法分析的基础,他把输入的字符串形式的源程序分割成一个个单词符号。为此P1./0编译程序设置了三个全程量的公用单元:SYM:存放每个单词的类别,用内部编码形式表示。ID:存放用户所定义的标识符的值。及标识符字符串的机内表示。NUM:存放用户定义的数。单词的种类有五种:基本字:也称为保留字,如BEGlN,END,IF,THEN等运算符:如+、-、*、/、:=、#、=V=等。标识符:用户定义的变量名、常数名、过程名。常数:如10,25,100等整数。界符:如,、.、;、(、)等如果把基本字、运算符、界符称为语言固有的单词,而对标识符、常数称为用户定义的单词。那么经词法分析程序分出的单词,对语言固有的单词只给出类别存放在SYM中,而对用户定义的单词既给出类别又给值,其类别放在SYM中,值放在ID或NUM中,全部单词种类由编译程序定义的纯量类型SYMBO1.给出。词法分析程序GETSYM将完成下列任务:(1)空格;(2)识别保留字;(3)识别标识符;(4)拼数;(5)拼复合词,如:=、:=、二等单词;(6)输出源程序在GETSYM中要调用GETCH,其功能是取字符。其过程框图如下:GErCH)联八一1J/13ErrW1.iX1.lnl£中并输出,三CC三=OCC三=CC+1CH三=1.INECC(W3说明:CH:存放当前读取的字符,初值为空;1.ineBuffer:一维数组,其数组元素是字符,界对为1:80。用于读入一行字符的缓冲区;1.ineNunICharCoul:计数器,初值为0;Token:一维数组,其数组元素是字符,界对1:10;ID:同A;Word:保留字表,一维数组,数组元素为以字符为元素的一维数组(即字符串),界对为1:13(扩展为23个),查找方式为二分法。7.3 P1.o集成开发环境工作流程 打开PK)语言文件 设置是否单步编译或单步运行 执行编译比控制台P1.O编译过程增加断点,其余基本相同。 运行P-code代码 输出结果和DEBUG信息8源程序和试用相8.1 源程序详见P10Src,P10Win32Src下文件。8.2 系毓测试输出方式:源程序及生成P-Code代码在控制台和文件(OUtPUt.txt洞时输出,同时还输出编译过程信息(debug.txt)。详见测试文档9开发体会心得9.1 编译学习体会早就知道编译原理不好学,它的理论性很强。课程内容很多,而且又非常抽象。那一大堆的1.R(0)、S1.R、1.R(1)、1.A1.R(I)分析法,还有烦人的属性翻译文法(幸亏不是考试重点)。才学这些我是一头雾水,难以理解。但在经过考试前的一阵更习,略知一二。同时也开始学习教材后的P1.O编译系统。9.2 项目实践心得在分析N.Wirth的P1.O文件的同时,我便开始尝试用C+来改写,也考虑怎么对其进行扩充。虽然只是改写,但也考察了对C+语言设施的掌握和运用能力。在此期间,我搜索互联网,看C+编程思想,Smsdn,请教高手(这还得谢谢班上几个强人),终于在元旦时就基本完成。在VC6.0上编写,当然BUG多多。后来,由于考试的压力,投入的时间越来越少,基本搁置。放假在家由于没有编程环境,仍旧搁置中。开学前才开始重新在VC.NET2003的VC+7.Ih编写调试P1.O系统,开始用C+的思想来封装,但由于对面向对象的思想的理解不深,设计还不很合理。这时对编译器的构造过程有了进一步的了解,同时也发现了原来改写的很多BUGo最终版本终于达到V1.0.看到别人做的P1.O的IDE,觉得实现起来并不是很复杂。便有了为封装的P1.O来做一个【DE,因此便有了P1.o集成开发环境P1.oDE。原本打算运用假期所学的VC的MFC进行开发,但实在找不到在MFC中挂载原有类的方法,只好放弃这一构想,转到在BOrIanC+Builder6.0上实现,这样即可以运用以前的经验,又可以快速实现。由于时间仓促和能力有限,找不到设置断点的有效文法,只好采用用MessageBox来达到进程等待的目的(用多线程可以实现按钮来控制),另一点不足就是没并实现代码编辑功能,这个可能并不复杂。最终版本也达到了V0.8o在开发这个项目的过程中,我投入了很多时间和精力,也遇到了很多困难,但我相信有付出就有回报,也使我明白坚定的信息在工作中的重要性。我从中学到了很多知识,运用所学的编译理论,实践C+编程,使用强大的VC开发环境,Visio进行建模等等。这些经验对我以后参加实验室的大项目和工作有莫大的帮助。9.3 开发日志/*P1.Ocompilerenviroment:win2003+VC6.0/VC7.12004.12.091.StartcodingP10,Readbookfirst.2004.12.25I.50%coding.2.unrunable2004.12.30-ITRunnerP10V0.11.80%2.runable,bedugisenough.3.对P1.O的理解很重要.2005.1.1- ITRunnerPlOV0.21.85%2.己可以生成正确的P-CODE代码,但是在B1.OCK出口处的TEST有错3.粗心大意是程序员的最大忌讳.4.掌握好的调试方法2005.1.2- ITRunnerP10VO.31.另入程序执行输出2.P-CODE解释执行实现3.测试两个源程序,通过。2005.1.18-ITRunnerPlOV0.42005.2.26-2005.2.28-V0.5后面的test测试通过,2005.3.1-V0.8I.修改调试信息输出方式,更好的信息输出。1.开学了,认真准备大作业。2.1 .修正block中递归分析参数传递的错误,使2 .增设DEBUG重载函数。3 .宏定义修改。I.增设数据类型,char,boolean,real,string.2 .修改类型检查,扩展类型3.增加的数据类型的运算功能并未完全实现,2005.33-V0.91.调整程序架构,使程序结构更美观.3 .所有输出信息使用宏做开关。3.对P1.O类进行进行一点调整。4.进行全面测试,7个测试用例。排除一定的BUG。20053.4-V0.922005.3.4-V0.942005.3.4-V0.9820053.5-V0.981 .改进FOR的实现算法。2 .改进输出,和符号表的初始化问题。3 .改进输出,和符号表的初始化问题。I.factor中调用test处仍有BUGo*/10附录3.1 1参考资料 编译原理及编译程序构造高仲仪,金茂忠北航出版社 编译原理吕映芝,张素琴等清华出版社 现代编译器设计. C程序设计语言Kernighan,Ritchie机械工业出版社 C+编程思想(第一卷:标准C+导引)美BrUCeECkel机械工业出版社 MSDN1.ibrary-October2004Microsoft