一、实验目的
编制一个读单词过程,从输入的源程序中,识别出各个具有独立意义的单词,即基本保留字、标识符、常数、运算符、分隔符五大类。并依次输出各个单词的内部编码及单词符号自身值。
二、实验题目
如源程序为C语言。输入如下一段:
main()
{
int a=-5,b=4,j;
if(a>=b)
j=a-b;
else j=b-a;
}
要求输出如下:
(2,”main”)(5,”(”) (5,”)”)
(5,”{”) (1,”int”) (2,”a”)
(4,”=”) (3,”-5”) (5,”,”)
(2,”b”) (4,”=”) (3,”4”)
(5,”,”) (2,”j”) (5,”;”)
(1,”if”) (5,”(”) (2,”a”)
(4,”>=”) (2,”b”) (5,”)”)
(2,”j”) (4,”=”) (2,”a”)
(4,”-”) (2,”b”) (5,”;”)
(1,”else”)(2,”j”) (4,”=”)
(2,”b”) (4,”-”) (2,”a”)
(5, ”;”) (5,”}”)
三、实验理论依据
(一)识别各种单词符号
1、 程序语言的单词符号一般分为五种:
1) 关键字(保留字/ 基本字)if 、while 、begin…
2) 标识符:常量名、变量名…
3) 常数:34 、56.78 、true 、‘a’ 、…
4) 运算符:+ 、- 、* 、/ 、〈、and 、or 、….
5) 界限符:, ; ( ) { } /*…
2、 识别单词:掌握单词的构成规则很重要
1) 标识符的识别:字母| 下划线+( 字母/ 数字/ 下划线)
2) 关键字的识别:与标识符相同,最后查表
3) 常数的识别
4) 界符和算符的识别
3、 大多数程序设计语言的单词符号都可以用转换图来识别,如图1-1
图1-1
4、 词法分析器输出的单词符号常常表示为二元式:(单词种别,单词符号的属性值)
2) 关键字可视其全体为一种,也可以一字一种。采用一字一种得分法实际处理起来较为方便。
3) 标识符一般统归为一种
4) 常数按类型(整、实、布尔等)分种
5) 运算符可采用一符一种的方法。
6) 界符一般一符一种的分法。
(二)超前搜索方法
1、 词法分析时,常常会用到超前搜索方法。
如当前待分析字符串为“a>+” ,当前字符为“>” ,此时,分析器倒底是将其分析为大于关系运算符还是大于等于关系运算符呢?
显然,只有知道下一个字符是什么才能下结论。于是分析器读入下一个字符’+’ ,这时可知应将’>’ 解释为大于运算符。但此时,超前读了一个字符’+’ ,所以要回退一个字符,词法分析器才能正常运行。又比如:‘+’ 分析为正号还是加法符号。
(三)预处理
预处理工作包括对空白符、跳格符、回车符和换行符等编辑性字符的处理,及删除注解等。由一个预处理子程序来完成。
四、词法分析器的设计
1、 设计方法:
1) 写出该语言的词法规则。
2) 把词法规则转换为相应的状态转换图。
3) 把各转换图的初态连在一起,构成识别该语言的自动机
4) 设计扫描器
2、 把扫描器作为语法分析的一个过程,当语法分析需要一个单词时,就调用扫描器。扫描器从初态出发,当识别一个单词后便进入终态,送出二元式
图1-2 取单词程序框图
五、程序
#include<stdio.h> #include<string.h> #include <ctype.h> #include <malloc.h> #include <stdlib.h> #define NULL 0 FILE *fp; char cbuffer; char *key[34]= {"if","else","for","while","do","return","break","continue","auto","double", "int","struct","long","switch","case","enum","register","typedef","char","extern" "union","const","float","short","unsigned","signed","void","default","goto","sizeof", "volatile","static","inline","restrict" }; int atype,id=4; /*1:关键字 2:标识符 3:常数 4:运算符 5:界符*/ int search(char searchchar[ ],int wordtype) /*判断单词是保留字还是标识符*/ { int i=0; int p; switch (wordtype) { case 1: for (i=0; i<=32; i++) { if (strcmp(key[i],searchchar)==0) { p=i+1; /*是保留字则p为非0且不重复的整数*/ break; } else p=0; /*不是保留字则用于返回的p=0*/ } return(p); } } char alphaprocess(char buffer) { int atype; /*保留字数组中的位置*/ int i=-1; char alphatp[20]; while((isalpha(buffer))||(isdigit(buffer))||buffer=='_') { alphatp[++i]=buffer; buffer=fgetc(fp); } /*读一个完整的单词放入alphatp数组中*/ alphatp[i+1]='\0'; atype=search(alphatp,1);/*对此单词调用search函数判断类型*/ if(atype!=0) { printf("%s, (1.关键字,%d)\n",alphatp,atype-1); id=1; } else { printf("(%s ,2.标识符)\n",alphatp); id=2; } return(buffer); } char digitprocess(char buffer) { int i=-1; char digittp[20]; while ((isdigit(buffer))||buffer=='.'||buffer=='e')//1.小数点 2.科学计数法 { digittp[++i]=buffer; buffer=fgetc(fp); } digittp[i+1]='\0'; printf("(%s ,3.常数)\n",digittp); id=3; return(buffer); } char otherprocess(char buffer) { char ch[20]; ch[0]=buffer; ch[1]='\0'; if(ch[0]==','||ch[0]==';'||ch[0]=='{'||ch[0]=='}'||ch[0]=='('||ch[0]==')')/*界符*/ { printf("(%s ,5.界符)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } if(ch[0]=='*'||ch[0]=='/'||ch[0]=='%') /*3.取余标识符*/ { if(ch[0]=='%') /*4.格式说明*/ { buffer=fgetc(fp); if(buffer=='c'||buffer=='s'||buffer=='d'||buffer=='f') { ch[1]=buffer; ch[2]='\0'; printf("(%s,格式说明)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } } if(ch[0]=='*')/*5.*=,/=*/ { buffer=fgetc(fp); ch[1]=buffer; if(ch[1]=='=') { ch[2]='\0'; printf("(%s,4.运算符)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } } if(ch[0]=='/') { buffer=fgetc(fp); ch[1]=buffer; if(ch[1]=='=') { ch[2]='\0'; printf("(%s,4.运算符)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } if(ch[1]=='*'||ch[1]=='/')/*6.注释*/ { int i=1; if(ch[1]=='*') { while(ch[i]!='/') { buffer=fgetc(fp); ch[++i]=buffer; } ch[i+1]='\0'; printf("(%s ,注释)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } else { while(ch[i]!='\n') { buffer=fgetc(fp); ch[++i]=buffer; } ch[i]='\0'; printf("(%s ,注释)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } } } printf("(%s ,4.运算符)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } if(ch[0]=='='||ch[0]=='!'||ch[0]=='<'||ch[0]=='>') { buffer=fgetc(fp); if(buffer=='=') { ch[1]=buffer; ch[2]='\0'; printf("(%s ,4.运算符)\n",ch); } else { printf("(%s ,4.运算符)\n",ch); id=4; return(buffer); } buffer=fgetc(fp); id=4; return(buffer); } if(ch[0]=='+'||ch[0]=='-') { if(id==4) /*在当前符号以前是运算符,则此时为正负号*/ { buffer=fgetc(fp); int i=1; ch[i]=buffer; /*7.多位整数*/ if(isdigit(ch[i])) { while(isdigit(ch[i])||ch[i]=='e'||ch[i]=='.') { buffer=fgetc(fp); ch[++i]=buffer; } ch[i]='\0'; printf("(%s ,3.常数)\n",ch); id=3; //buffer=fgetc(fp); id=4; return(buffer); } buffer=fgetc(fp); id=4; return(buffer); } if(ch[0]=='+')/*8.自增...++、+=、-=,--*/ { buffer=fgetc(fp); ch[1]=buffer; if(ch[1]=='='||ch[1]=='+') { ch[2]='\0'; printf("(%s,4.运算符)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } ch[1]='\0'; printf("(%s ,4.运算符)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } if(ch[0]=='-') { buffer=fgetc(fp); ch[1]=buffer; if(ch[1]=='='||ch[1]=='-'||ch[1]=='>')//9.指向结构体成员运算符 { ch[2]='\0'; printf("(%s,4.运算符)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } ch[1]='\0'; printf("(%s ,4.运算符)\n",ch); //buffer=fgetc(fp); id=4; return(buffer); } } if(ch[0]=='.')/*10.多位小数*/ { buffer=fgetc(fp); int i=1; ch[i]=buffer; if(isdigit(ch[i])) { while(isdigit(ch[i])) { buffer=fgetc(fp); ch[++i]=buffer; } ch[i]='\0'; printf("(%s ,3.常数)\n",ch); id=4; return(buffer); } } if(ch[0]=='#')//11.include<>头文件 { buffer=fgetc(fp); int i=1; ch[i]=buffer; if(ch[i]=='i') { while(ch[i]!='>') { buffer=fgetc(fp); ch[++i]=buffer; } ch[i+1]='\0'; printf("(%s ,#include<>)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } if(ch[i]=='d') /*12.define*/ { while(isalpha(ch[i])) { buffer=fgetc(fp); ch[++i]=buffer; } ch[i+1]='\0'; printf("(%s ,#define宏定义)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } } if(ch[0]=='&'||ch[0]=='^'||ch[0]=='|') /*13.逻辑运算符*/ { buffer=fgetc(fp); if(buffer=='&') { ch[1]=buffer; ch[2]='\0'; buffer=fgetc(fp); printf("(%s ,4.运算符)\n",ch); id=4; return(buffer); } else if(buffer=='|') { ch[1]=buffer; ch[2]='\0'; printf("(%s ,4.运算符)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } else { printf("(%s ,4.运算符)\n",ch); id=4; return(buffer); } } if(ch[0]=='['||ch[0]==']')/*14.下标运算符*/ { printf("(%s ,4.运算符)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } if(ch[0]=='\''||ch[0]=='\"')/*15.引号*/ { printf("(%s ,引号)\n",ch); buffer=fgetc(fp); id=4; return(buffer); } if(ch[0]=='\\')/*16.转义字符*/ { buffer=fgetc(fp); if(buffer=='n'||buffer=='t'||buffer=='b'||buffer=='r'||buffer=='f'||buffer=='\\'||buffer=='\''||buffer=='\"') { ch[1]=buffer; ch[2]='\0'; printf("(%s ,转义字符)\n",ch); } buffer=fgetc(fp); id=4; return(buffer); } } int main() { if ((fp=fopen("H:\\example.c","r"))==NULL) /*只读方式打开一个文件*/ printf("error"); else { printf("1:关键字 2:标识符 3:常数 4:运算符 5:界符\n"); cbuffer = fgetc(fp); /*fgetc( )函数:从磁盘文件读取一个字符*/ while (cbuffer!=EOF) { if(cbuffer==' '||cbuffer=='\n'||cbuffer=='\t') /*掠过空格和回车符*/ /*17.tab键*/ cbuffer=fgetc(fp); else if(isalpha(cbuffer)) cbuffer=alphaprocess(cbuffer); else if (isdigit(cbuffer)) cbuffer=digitprocess(cbuffer); else cbuffer=otherprocess(cbuffer); } } return 0; }
六、结果