一.前言
某属于在校大学生,几天前老师布置了一个编译原理作业,将词法分析–部分实现,头疼,众所周之,编译原理是计算机专业中最令人头疼的课程,听懂已经很不容易了,TMD让我用C语言实现 ,头大。经过几个小时写了一个大概,已经完成了老师的初步要求。废话不多说。直接干!!
二.要求及内容
一、授课内容:
(一) 授课科目:编译原理
(二) 授课内容:实验一 词法分析
(三) 授课类型:实 验
(四) 授课时间:4学时(计划)
二、教学目的要求:
1.目的:通过设计、编制、调试一个具体的词法分析程序加深对词法分析原理的理解,并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法。
2.要求:
(1)选择在国际国内有代表性的高级程序设计语言的源程序作为词法分析对象。
(2)根据数学要求和学生具体情况,从上列语言之一中选取一个适当大小的子集,可以选取一类典型单词,也可以尽可能使各种类型的单词都能兼顾到。
三、教学设想:
1.教学方法设想:先以例子讲解,然后学生动手实验,实验为主。
2.教具运用设想:多媒体。
四、教学过程:
1. 题目 试用直接分析方法编制C语言子集的词法分析程序。其BNF定义如下:
〈PASCAL子集程序〉::=〈变量说明〉BEGIN〈语句表〉ENG。
〈变量说明〉::=〈空〉| VAR〈变量表〉:〈类型〉
〈变量表〉::=〈变量〉|〈变量表〉,〈变量〉
〈类型〉::=〈标识符〉
〈语句表〉::=〈语句〉|〈语句表〉〈语句〉
〈语句〉::=〈赋值语句〉|〈条件语句〉|〈WHILE语句〉|〈复合语句〉|〈过程定义〉
〈赋值语句〉::=〈变量〉::=〈算术表达式〉
〈条件语句〉::=〈IF〉〈布尔表达式〉THEN〈语句〉ELSE〈语句〉
〈WHILE语句〉::=WHILE〈布尔表达式〉DO〈语句〉
〈复合语句〉::=BEGIN〈语句表〉END
〈过程定义〉::=PROCEDURE〈标识符〉〈参数表〉BEGIN〈语句表〉END
〈参数表〉::=〈空〉|(〈标识符表〉)
〈标识符表〉::=〈标识符〉|〈标识符表〉,〈标识符〉
〈算术表达式〉::=〈项〉|〈算术表达式〉+〈项〉
〈项〉::=〈初等量〉|〈项〉*〈初等量〉
〈初等量〉::=〈无符号数〉|〈变量〉|(〈算术表达式〉)
〈布尔表达式〉::=〈算术表达式〉〈关系符〉〈算术表达式〉
〈变量〉::=〈标识符〉
〈标识符〉::=〈字母〉|〈标识符〉〈字母〉|〈标识符〉〈数字〉
〈无符号数〉::=〈数字〉|〈无符号数〉〈数字〉
〈关系符〉::=〈 = |〈 〉| 〉
〈字母〉::=A | B| C| D| E | F| G | H | I | J | K | L| M | N | O | P|Q | R |S | T| U| V| W| X| Y| Z
〈数字〉::=0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
〈空〉::=
词法分析是编译程序的第一个处理阶段。这里所谓直接分析方法,即自左至右扫描源程序,一旦发现有独立意义的字符串时,立即将其改造成长度统一的最小语法单位,同时查填各类单词表格并做一些语法检查,为以后的语法分析提供方便。
具体的处理过程是,在扫描字符串时,一旦识别出关键字(K)、标识符(I)、常数(C)和界符(P)中之一,即以单词形式(剔去多余的空白符)输出。每次调用词法分析程序,它均能自动继续扫描下去,形成下一个单词,直至整个源程序全部扫描完毕,习惯年成相应的单词串。
各类单词均有相同的结构和长度。每个单词由两部分组成: (t, i)
其中t表示单词种类。共分四类,即K类、I类、C类和P类。每类对应一种表格,分别存放该类各个不同的单词。I为指向该类表格一个特定项目的指针。因此,(t, i)唯一地确定了一个单词。
K,P两种表格的内容取决于所选语言的子集,而I,C两种表格则是根据临时输入的源程序字符串形成的。
2.算法 词法分析程序在扫描过程中,依次从源程序驱除源字符,并根据第一个字符(有时还需多读一个字符)判断属于K,I,C,P中的哪一类单词,确定单词的t和i 。在词法分析过程中,K、P两表是固定不变的(由语言来确定),源程序字符串只能从其中选取。I、C两表是在分析过程中不断形成的。其词法分析的算法如图2-7-2所示。
为了防止实习良过大,达不到实习的目的,实习时采用的数据结构在不同程度上均应作适当的简化,所选的关键字(K)和界符表(P)
如表2-7-1和表2-7-2所示
关键字表包括九个代表性的关键字。界符表包括关系运算符三种(其中小于等于和不等于均系由两个字符组成的符合字符),算术运算符和分隔符各两种,圆括号一对,加上赋值号共十一种。这两个表的内容表明,PASCAL语言的赋值语句、条件语句、WHILE型循环语句、复合语句过程及变量说明均可作为源程序例子输入给词法分析程序。标识符表中的每一项包含一个标识符。常数表中的每一项包含一个整常数。后两表都是在词法分析过程中产生的。
三.说明
本程序是对C语言进行词法分析,对老师的要求进行了改编
ci 常数表
k 关键字表
id 标识符表
p 界符表
实现功能(在基本功能之上):
1.可以分析小数
2.可以循环
3. 可以识别标识符
附图:
四.实现
#include <stdio.h>
#include<math.h>
#define keylen 10
#define identlen 10
#include<stdlib.h>
typedef struct {
char ty;
int point;
} outreco;
char flag_a;
int flag=1;//标志位,1为继续,0为不继续
int cip=0,ip=0,pint=0,i,j,l,m,errors=0;
char char1;
double ci[10]={0,0,0,0,0,0,0,0,0,0};
char k[10][10]={"begin","while","for","if","else","long","case","do","main","int"},id[10][10]={""},token[10]={' '},instring[80];
char p[11][3]={"<=","<>","<","(","*","==","=","+",")",";",","};
outreco outtoken;
void GetChar()
{
char1=instring[pint];
pint++;
}
void error()
{
errors=1;
printf("error!\n");
system("PAUSE");
// pint++;
}
void lexical()//词法分析
{
double num;
int l,m=0,_num,b,i,len=0;
for(i=0;i<identlen;i++) token[i]='\0';
errors=0;
GetChar();
while(char1==' ') GetChar(); //过滤空格
if(char1>='a'&&char1<='z'||char1>='A'&&char1<='Z'||char1=='_')
{
while(char1>='a'&&char1<='z'||char1>='0'&&char1<='9'||char1>='A'&&char1<='Z'||char1=='_')
{
if(m<identlen)
{
token[m]=char1;
m++;
}
GetChar();
} //end of while
pint--;
l=0;b=0;
while(l<keylen && !b)//属于关键字情况
{
b=1;i=0;
while(i<identlen && b)
if( k[l][i]==token[i]) i++;
else b=0;
if(!b) l++;
} //end of while
if(l<keylen)
{
outtoken.ty='k';
outtoken.point=l+1;
}
else //属于标识符的情况
{
l=0;b=0;
while(l<ip && !b)
{
b=1;i=0;
while(i<identlen && b)
if( id[l][i]==token[i]) i++;
else b=0;
if(!b) l++;
} //end of while
if(l<ip)
{
outtoken.ty='i';
outtoken.point=l+1;
}else if(l>=ip)//标识符表中没有此标识符
{
for(m=0;m<identlen;m++)
id[ip][m]=token[m];
ip=ip+1;
outtoken.ty='i';
outtoken.point=l+1;
}
}
} //end of if character
else
if(char1 >='0'&&char1<='9'||char1=='.')
{
num=0;
_num=0;//存放小数部分
while(char1 >='0'&&char1<='9')
{
num=num*10+char1-'0'; //整数
token[m++]=char1;
GetChar();
}
if(char1=='.'){
GetChar();
while(char1 >='0'&&char1<='9')
{
_num=_num*10+char1-'0'; //小数部分
len++;
token[m++]='.';
token[m++]=char1;
GetChar();
}
pint--;
num=num+_num/(pow(10,len));
}
l=-1;
do{
l++;
}while(l<cip && num!=ci[l]);
if(l==cip) //识别出一个新的数
{
ci[l]=num;
cip++;
}
outtoken.ty='c';
outtoken.point=l+1;
} //识别实数的过程结束
else
if( char1=='<'||char1=='('||char1=='*'||char1=='='||char1=='+'||char1==')'||char1==';'||char1==',')
{
outtoken.ty='p';
token[m++]=char1;
switch(char1)
{
case '<':GetChar();
if(char1!='='&&char1!='>')
{
outtoken.point=3;
pint--;
}
else
{
if(char1=='=') outtoken.point=1;
else outtoken.point=2 ;
token[m++]=char1;
}
break;
case '(':outtoken.point=4;
break;
case '*':outtoken.point=5;
break;
case '=':GetChar();
if(char1=='=')
{ token[m++]=char1;
outtoken.point=6;
}
else
{
outtoken.point=7;
pint--;
}
break;
case '+':outtoken.point=8;
break;
case ')':outtoken.point=9;
break;
case ';':outtoken.point=10;
break;
case ',':outtoken.point=11;
break;
}//end of switch
} //end of if p
else error();
} // end of lexical
void main()
{
//int i;
ip=0;
cip=0;
pint=0;
printf("\t******************按@键退出*********************************\n");
printf("\tK:");
for(i=0;i<10;i++)
printf("%6s",k[i]);
printf("\n");
printf("\t**************************************************************\n");
printf("\tP:");
for(i=0;i<11;i++)
printf("%5s",p[i]);
printf("\n");
printf("\t**************************************************************\n");
printf("source, input!\n");
//scanf("%s",instring);
gets(instring);
// pint=0;
// if(instring[pint]=='@'){
// break;
// }
do{
lexical();
if(!errors)
{
printf("(%c,%2d)\n",outtoken.ty,outtoken.point);//以二元组的形式输出单词
printf("识别的单词为:%s\n",token);
}
}while(instring[pint]!='\0');
system("PAUSE");
while(1){
//ip=-1;
//cip=0;
//pint=0;
// printf("continue?y/n:\n");
// scanf("%c",&flag_a);
// getchar();
// if(flag_a=='y')
// {
// flag=1;
// }
// else
// {
// flag=0;
// break;
// }
system("CLS");
printf("\t******************按@键退出*********************************\n");
printf("\tK:");
for(i=0;i<10;i++)
printf("%6s",k[i]);
printf("\n");
printf("\t**************************************************************\n");
printf("\tP:");
for(i=0;i<11;i++)
printf("%5s",p[i]);
printf("\n");
printf("\t**************************************************************\n");
printf("\ti:");
for(i=0;i<10;i++)
printf("%6s",id[i]);
printf("\n");
printf("\t**************************************************************\n");
printf("\tC:");
for(i=0;i<cip;i++)
printf("%6g",ci[i]);
printf("\n");
printf("\t**************************************************************\n");
printf("source, input!\n");
//scanf("%s",instring);
for(i=0;i<80;i++) instring[i]='\0';
//fflush(stdin);//清除缓存区让gets可以读取键盘输入的数据
gets(instring);
pint=0;
if(instring[pint]=='@'){
break;
}
do{
lexical();
if(!errors)
{
printf("(%c,%2d)\n",outtoken.ty,outtoken.point);//以二元组的形式输出单词
printf("识别的单词为:%s\n",token);
}
}while(instring[pint]!='\0');
system("PAUSE");
}
}