C语言词法分析器构造实验报告(1)1
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
C 语言词法分析器构造实验报告
02计算机(2) 2002374203 冯绍欣
一、题目要求:
完成一个C 语言的词法分析器的构造。
此词法分析器能识别附值语句、循环语句、条件语句、并能处理注释。
二、设计方案:
这个词法分析器分析的主要关键字有:main, int, float, char, if, else, for, while, do, switch, case, break; default 。
选择要分析的c 文件,首先对其去掉注释和与空格处理,再根据字符的不同类型分析。
1、全局数据结构:
字符数组 set[ ]:存放从文件中读到的所有字符;
str[ ]:存放经过注释处理和预空格处理的字符;
strtoken[ ]:存放当前分析的字符;
结构体 KEYTABLE :存放关键字及其标号;
全局字符变量 ch :当前读入字符;
全局整型变量 sr, to :数组str, strtoken 的指针。
2、以层次图形式描述模块的组成及调用关系
3、主要函数的设计要求(功能、参数、返回值):
openfile :打开文件;
GetChar :将下一个输入字符读到ch 中,搜索指示器前移一字符位置;
GetBC :检查ch 中的字符是否为空白。
若是,则调用GetChar 直至ch 中进入一个非空白字符; Main ( )
Openfile ( ) Analysis ( )
Reflesh() Process()
Set32()
GetChar() GetBC()
Concat()
Reserve()
IsLetter() IsDigit()
Retract()
GetChar()
Concat :将ch 中的字符连接到strtoken 之后;
IsLetter 和IsDigit :布尔函数过程,分别判断ch 中的字符是否为字母和数字;
Reserve :整型函数过程,对strtoken 中的字符串查找关键字表,若是关键字则返回编码,否则返回-1;
Retract :将搜索指示器回调一个字符位置,将ch 置为空白字符;
reflesh :刷新,把strtoken 数组置为空;
prearrange1:将注释部分置为空格;
prearrange2:预处理空格,去掉多余空格;
analysis:词法分析;
main :主函数。
4、状态转换图:
字母或数字
字母 非字母或数字
数字 数字 非数字
字符a
字符a 字符b
… = ‟
字符c
字符a 包括:= , & , | , + , --
字符b 包括:-- , < , > , | , *
字符c 包括:, , : , ( , ) , { , } , [ , ] , ! ,# , % , ” , / , * , + , -- , > , <, .
三、源代码如下:
#include <stdio.h>
#include <string.h>
char set[1000],str[500],strtoken[20];
char sign[50][10],constant[50][10];
char ch;
int sr,to,id=0,st=0;
typedef struct keytable /*放置关键字*/
{
char name[20]; 0 1 2 3 4 6
7 5 9 8
int kind;
}KEYTABLE;
KEYTABLE keyword[]={ /*设置关键字*/
{"main",0},
{"int",1},
{"float",2},
{"char",3},
{"if",4},
{"else",5},
{"for",6},
{"while",7},
{"do",8},
{"switch",9},
{"case",10},
{"break",11},
{"default",12},
};
openfile() /*打开文件*/
{
FILE *fp;
char a,filename[10];
int n=0;
printf("Input the filename:");
gets(filename);
if((fp=fopen(filename,"r"))==NULL)
{
printf("cannot open file.\n");
exit(0);
}
else
while(!feof(fp)) /*文件不结束,则循环*/
{
a=getc(fp); /*getc函数带回一个字符,赋给a*/
set[n]=a; /*文件的每一个字符都放入set[]数组中*/
n++;
}
fclose(fp); /*关闭文件*/
set[n-1]='\0';
printf("\n\n-------------------Source Code--------------------------\n\n");
puts(set);
printf("\n--------------------------------------------------------\n");
}
reflesh() /*清空strtoken数组*/
{
to=0; /*全局变量to是strtoken的指示器*/
strcpy(strtoken," ");
}
prearrange1() /*预处理程序1*/
{
int i,a,b,n=0;
do
{
if(set[n]=='/' && set[n+1]=='*')
{
a=n; /*记录第一个注释符的位置*/
while(!(set[n]=='*' && set[n+1]=='/'))
n++;
b=n+1; /*记录第二个注释符的位置*/
for(i=a;i<=b;i++) /**/
set[i]=' '; /*把注释的内容换成空格,等待第二步预处理*/ }
n++;
}while(set[n]!='\0');
}
prearrange2() /*预处理程序2*/
{
int j=0;
sr=0; /*全局变量sr是str[]的指示器*/
do
{
if(set[j]==' ' || set[j]=='\n')
{
while(set[j]==' ' || set[j]=='\n') /*扫描到有连续的空格或换行符*/
j++;
str[sr]=' '; /*用一个空格代替扫描到的连续空格和换行符放入str[]*/
sr++;
}
else
{
str[sr]=set[j]; /*若当前字符不为空格或换行符就直接放入str[]*/
sr++;
j++;
}
}while(set[j]!='\0');
str[sr]='\0';
}
char GetChar() /*把字符读入全局变量ch中,指示器sr前移*/
{
ch=str[sr];
sr++;
return(str[sr-1]);
}
void GetBC() /*开始读入符号,直至第一个不为空格*/
{
while(ch==' ')
{
ch=GetChar();
}
}
Concat() /*把ch中的字符放入strtoken[]*/
{
strtoken[to]=ch;
to++; /*全局变量to是strtoken的指示器*/
strtoken[to]='\0';
}
int IsLetter() /*判断是否为字母*/
{
if((ch>=65 && ch<=90)||(ch>=97 && ch<=122))
return(1);
else return(0);
}
int IsDigit() /*判断是否为数字*/
{
if(ch>=48 && ch<=57)
return(1);
else return(0);
}
int Reserve() /*对strtoken中的字符串查找保留字表,若是则返回它的编码,否则返回-1*/ {
int i,k=0;
for(i=0;i<=20;i++)
{
if(strcmp(strtoken,keyword[i].name)==0)
{ k=1;
return(keyword[i].kind);
}
}
if(k!=1)
return(-1);
}
void Retract() /*指示器sr回调一个字符位置,把ch置为空*/
{
sr--;
}
int InsertId()
{
int i,k;
for(i=0;i<id;i++)
{
k=strcmp(strtoken,sign[i]);
if(k==0)
return(i);
}
strcpy(sign[id],strtoken); /*插入标识符*/
id++;
return(id-1);
}
int InsertConst()
{
int i,k;
for(i=0;i<st;i++)
{
k=strcmp(strtoken,constant[i]);
if(k==0)
return(i);
}
strcpy(constant[st],strtoken); /*插入常数*/
st++;
return(st-1);
}
void analysis()
{
int value;
reflesh(); /*清空strtoken数组*/
prearrange1(); /*预处理,使注释内容换成单个空格,放回set[]中*/
prearrange2(); /*预处理,使set[]中连续的空格置换成单个空格,并把set[]的内容放到str[]中*/
GetChar();
GetBC(); /*读取第一个字符*/
while(ch!='\0') /*当不等于结束符,继续执行*/
{
if(IsLetter())
{
while(IsLetter() || IsDigit()) /*若第一个是字符,继续读取,直到出现空格*/
{
Concat();
GetChar();
}
Retract(); /*指示器sr回调一个字符位置,把ch置为空*/
value=Reserve(); /*对strtoken中的字符串查找保留字表,若是则返回它的编码,否则返回-1*/ if(value==-1) /*如果返回值是-1,那就是变量,把它输出*/
{
InsertId(); /*插入标识符*/
printf("\n%s",strtoken);
getch();
}
else /*否则就是关键字,也输出*/
{
printf("\n%s",strtoken);
getch();
}
reflesh();
}
else if(IsDigit())
{
while(IsDigit()) /*否则,若第一个是数字,继续读取,知道出现空格*/
{
Concat();
GetChar();
}
Retract();
InsertConst(); /*插入常数*/
printf("\n%s",strtoken);
getch();
reflesh();
}
else
switch(ch) /*否则,若是下面的符号,就直接把它输出*/
{
case ',':
case ';':
case '(':
case ')':
case '{':
case '}':
case '[':
case ']':
case '!':
case '#':
case '%':
case '"':
case '/':
case '*':
Concat();
printf("\n'%s'",strtoken);
getch();
reflesh();
break;
default:if(ch=='=' || ch=='&' || ch=='|' || ch=='+' || ch=='-') /*如果是这些符号,继续读取下一个*/ {
Concat(); /*判断是否为==,&&,||,++,--的情况*/
GetChar();
if(ch==strtoken[0])
Concat();
else
Retract();
printf("\n'%s'",strtoken);
getch();
reflesh();
break;
}
else if(ch=='+' || ch=='-' || ch=='<' || ch=='>' || ch=='!' || ch=='*')
{
Concat(); /*判断是否为+=,-=,<=,>=,!=,*=的情况*/
GetChar();
if(ch=='=')
Concat();
else
Retract();
printf("\n'%s'",strtoken);
getch();
reflesh();
break;
}
else
{
printf("Error!");
getch();
break;
}
}
GetChar();
GetBC();
}
}
main()
{
clrscr();
openfile();
analysis();
printf(“analysis is over!”);
}
五、测试结果:
1、分析文件test1.c中的程序:
Input the filename:test.c
*****************Original Code************************
/* HELLO.C -- Hello, world */
#include "stdio.h"
#include "conio.h"
main()
{
printf("Hello, world\n");
getch();
}
*****************************************************
'#'
include
'"'
stdio
'.'
h
'"'
'#'
include
'"'
conio
'.'
h
'"'
main
'('
')'
'{'
printf
'('
'"'
Hello
','
worldError!
n
'"'
')'
';'
getch
'('
')'
';'
'}'
Analysis is over!
六、实验总结:
这个程序主要参考书上关于词法分析器的设计。
在设计过程中仍有遇到很多困难,但经请教同学后,好多问题都并不是想象中的困难。
尽管如此,分析考虑还不全面,例如没有创建符号表和常数表。
这些情况将在在语法和语义分析时进行完善。