实验一(词法分析)..(7页).doc
-实验一(词法分析).-第 7 页南京信息工程大学实验(实习)报告一 实验目的1、 学会针对DFA转换图实现相应的高级语言源程序。2、 深刻领会状态转换图的含义,逐步理解有限自动机。3、 掌握手工生成词法分析器的方法,了解词法分析器的内部工作原理。二 实验内容计算机程序设计语言的编译程序的词法分析部分实现。给出算法的流程图及有穷状态自动机的模型(可以用矩阵或者状态图表示)从左到右扫描每行该语言源程序的符号,拼成单词,换成统一的内部表示(token)送给语法分析程序。为了简化程序的编写,有具体的要求如下:(1) 数仅仅是整数。(2) 空白符仅仅是空格、回车符、制表符。(3) 代码是自由格式。(4) 注释应放在花括号之内,并且不允许嵌套三 实验要求要求实现编译器的以下功能:(1) 按规则拼单词,并转换成二元式形式(2) 删除注释行(3) 删除空白符 (空格、回车符、制表符)(4) 列表打印源程序,按照源程序的行打印,在每行的前面加上行号,并且打印出每行包含的记号的二元形式(5) 发现并定位错误词法分析进行具体的要求:(1) 记号的二元式形式中种类采用枚举方法定义;其中保留字和特殊字符是每个都一个种类,标示符自己是一类,数字是一类;单词的属性就是表示的字符串值。(2) 词法分析程序当识别一个单词完毕,采用返回值的形式返回符号的种类,同时采用程序变量的形式提供当前识别出记号的属性值。(3) 标示符和保留字的词法构成相同,为了更好的实现,把语言的保留字建立一个表格存储,这样可以把保留字的识别放在标示符之后,用识别出的标示符对比该表格,如果存在该表格中则是保留字,否则是一般标示符。(选做)四 实验步骤(1)词法分析器的设计方法有如下四个步骤: 1.写出该语言的词法规则。 2.把词法规则转换为相应的状态转换图。 3.把各转换图的初态连在一起,构成识别该语言的自动机。 4.设计扫描器;把扫描器作为语法分析的一个过程,当语法分析需要一个单词时,就调用扫描器。扫描器从初态出发,当识别一个单词后便进入终态,送出二元式。 词法分析需要一个单词时,扫描器取单词的程序流程图如下: (2)词法分析器的输出形式 词法分析器输出的单词符号常常表示为二元式:(单词种别,单词符号的属性值) 单词种别通常用整数编码。 标识符一般统归为一种。常数按类型(整、实、布尔等)分种。关键字可视其全体为一种,也可以一字一种。采用一字一种的分法实际处理起来较为方便。运算符可采用一符一种的方法。至于界符一般也采用一符一种的分法。 (3)核心代码及其功能 #include "ctype.h"#include "string.h"#include "stdio.h" /*定义一个全局变量,一个全局指针*/FILE *fp; /*fp是一个指向FILE类型结构的指针变量,可以使fp指向某一个文件 的结构变量,从而通过该结构体变量中的文件信息访问该文件*/int id; /*标志变量,用来标识各类型的ID*/*函数声明*/int search(char searchchar,int wordtype); char alphaprocess(char buffer);char digitprocess(char buffer);char otherprocess(char buffer);void error();/*主函数*/void main()/*自定义函数的声明*/char cbuffer;if (fp=fopen("example.cpp","r")=NULL) /*以只读方式打开文件"example.c",NULL在 stdio.h文件中已被定义为0*/printf("error");else cbuffer=fgetc(fp); /*文件不为空则从文件中取字符*/while (cbuffer!=EOF) /*EOF文件结束标志*/if(cbuffer=' '|cbuffer='n'|cbuffer='t') /*掠过空格和回车符*/cbuffer=fgetc(fp);/id=4;else if(isalpha(cbuffer) cbuffer=alphaprocess(cbuffer); /*检查cbuffer是否为字母,是则调用alphaprocess()函数*/else if (isdigit(cbuffer)cbuffer=digitprocess(cbuffer); /*检查cbuffer是否为数字09,是则调用digitprocess()函数*/else cbuffer=otherprocess(cbuffer); /*非上述两者则调用otherprocess()函数*/*主函数结束*/*处理读取字符为字母的情况*/char alphaprocess(char buffer)int atype;int i=-1;char alphatp20; /*字符数组存储从文件中读取的字符*/while(isalpha(buffer)|(isdigit(buffer)|buffer='_'|buffer='.') /*标识符的组成成分*/alphatp+i=buffer; /*将当前读取的字符存如数组*/buffer=fgetc(fp); /*读取下一个字符*/alphatpi+1='0' /*字符串以'0'作为结束标志*/atype=search(alphatp,1); /*调用函数,判断当前字符串是否为关键字*/ if(atype!=0) /*是关键字则输出该关键字,编号为1,并输出该关键字在关键字表中的位子*/printf("(%s, (1,%d)n",alphatp,atype); id=1; /*关键字的ID为1*/elseprintf("(%s ,2)n",alphatp); /*为标识符时,编号为2*/id=2; /*标识符的ID为2*/return(buffer); /*判断字符串是否为关键字*/int search(char searchchar,int wordtype)char *key32="auto","break","case","char","const","continue","default","do","double","else", "enum","extern","float","for","goto","if","int","long","register","return","short","signed","sizeof","static","struct","volatile","while","switch","typedef","union","unsigned","void"/*设置数组指针存储c语言中的32个关键字*/int i;int p;switch (wordtype)case 1:for (i=0;i<=31;i+) if (strcmp(keyi,searchchar)=0) /*比较字符串,为关键字则定位该关键字的序号*/ p=i+1; break; else p=0;return(p);/*alphaprocess()函数结束*/*处理读取字符为数字时的情况*/char digitprocess(char buffer)int i=-1;char digittp20;int flag=0;while (isdigit(buffer)|buffer='.'|buffer='e'|buffer='E')/考虑数字为小数和指数时的情况 if(buffer='.'|buffer='e'|buffer='E')flag+;digittp+i=buffer;buffer=fgetc(fp); /*同上*/digittpi+1='0'if(flag>1) error();else printf("(%s ,3)n",digittp); /*输出该数字,编号为3*/ id=3; /*设置ID为3*/return(buffer);/*digitprocess()函数结束*/*处理读出字符为其他字符的情况*/char otherprocess(char buffer)int n=0;char ch20;ch0=buffer;ch1='0'if(ch0='%'|ch0='') buffer=fgetc(fp);ch1=buffer;ch2='0'printf("(%s ,4)n",ch); /*识别运算符% */id=4;buffer=fgetc(fp); return(buffer);if(ch0='&') buffer=fgetc(fp);if(buffer!='&') printf("(%s ,4)n",ch); /*识别引用运算符&*/ id=4;return(buffer);if(buffer='&') ch1=buffer;ch2='0'printf("(%s ,4)n",ch); /*识别关系运算符&&*/id=4;buffer=fgetc(fp); return(buffer);if(ch0=','|ch0=''|ch0=''|ch0=''|ch0='('|ch0=')')printf("(%s ,5)n",ch); /*分界符,;()的id标记为5*/buffer=fgetc(fp); id=5; return(buffer);if(ch0='*'|ch0='/') /*运算符 '*''/'的id标记为4*/printf("(%s ,4)n",ch);buffer=fgetc(fp); id=4; return(buffer); if(ch0='='|ch0='!'|ch0='<'|ch0='>')buffer=fgetc(fp);if(buffer='=') /*防止'=','!=','<=','>='符号的分离*/ch1=buffer;ch2='0'printf("(%s ,4)n",ch); /*'=','!=','<=','>='的标记为4 */else printf("(%s ,4)n",ch); /*'=','!','<','>'的标记为4*/id=4;return(buffer); buffer=fgetc(fp); id=4;return(buffer);if(ch0='+'|ch0='-')if(id=4) /*如果'+','-'前ID为4的字符则可能为正负数或'+','-',否则为加减号*/for(int i=1;i<10;i+)buffer=fgetc(fp);if(buffer='+')ch1='+'ch2='0'id=4;goto L;break; /*跳出for循环体*/if(buffer='-')ch1='-'ch2='0'id=4;goto L;break; /*跳出for循环体*/if(buffer=',')|(buffer='')|(buffer='+')|(buffer='-')|(buffer=')') break; /*防止将+12;+12,+12+,+12-,+12)作为整体输出*/chi=buffer; chi+1='0'printf("(%s ,3)n",ch); /*识别特殊数字*/id=3; /buffer=fgetc(fp);return(buffer);ch2='0'L: printf("(%s ,4)n",ch); /*识别特殊运算符 + -*/ buffer=fgetc(fp); id=4; return(buffer);/*otherprocess()函数结束*/void error()printf("程序出错n");/被测程序#include "ctype.h"char test(char buffer)int i=-1;1.1.0;double j=i+;if (i&&j) i=j;return(buffer);(3)实验结果的屏幕截图五 实验总结在整个编译器设计过程中,遇到了很多意想不到的困难,其主要原因是对各个部分要实现的功能考虑不够周全,典型的如在词法分析器的设计中,当前待分析字符串为“a>+”,当前字符为>,此时,分析器到底是将其分析为大于关系运算符还是大于等于关系运算符呢?又比如,+分析为正号还是加法符号,以及对空白符、跳格符、回车符和换行符等编辑性字符的处理,及删除注解等。这些在程序设计初期实现都比较困难,在前个问题中,我们用到了超前搜索方法,当当前待分析字符串出现“a>+”,当前字符为>时,分析器读入下一个字符+,这时可知应将>解释为大于运算符。但此时,超前读了一个字符+,所以要回退一个字符,词法分析器才能正常运行。