实验一 词法分析程序设计与实现
一.实验目的
通过编写和调试一个词法分析程序,掌握在对程序设计语言的源程序进行扫
描的过程中,将字符流形式的源程序转化为一个由各类单词构成的序列的词法分
析方法。
二.基本实验内容与要求
假定一种高级程序设计语言中的单词主要包括关键字 begin、end、if、then、
else、while、do;标识符;浮点常数;六种关系运算符;一个赋值符和四个算术
运算符,试构造能识别这些单词的词法分析程序(各类单词的分类码可参见表 1)。
输入:由符合和不符合所规定的单词类别结构的各类单词组成的源程序文件。
输出:把所识别出的每一单词均按形如(CLASS,VALUE)的二元式形式
输出,并将结果放到某个文件中。对于标识符和浮点常数,CLASS 字段为相应
的类别码的助记符;VALUE 字段则是该标识符、常数的具体值;对于关键字和
运算符,采用一词一类的编码形式,仅需在二元式的 CLASS 字段上放置相应单
词的类别码的助记符,VALUE 字段则为“空”。
要求:
1、上机前完成词法分析程序的程序流程设计,并选择好相应的数据结构。
2、用于测试扫描器的实例源文件中至少应包含两行以上的源代码。
3、对于输入的测试用例的源程序文件,词法正确的单词分析结果在输出文
件中以二元式形式输出,错误的字符串给出错误提示信息。
4、扩充关键字的数目、增加逻辑运算符等单词类别、将常数再细分成字符
串常量、整型常量和无符号数常量等;添加词法分析中单词出错的位置和错误类
型,以及删除注释部分等
三.设计过程
语言中的各类单词符号及其分类码表:
表 1 语言中的各类单词符号及其分类码表
单词符号 类别编码 类别码的助记符
单词值
begin
end
if
then
else
1
2
3
4
5
BEGIN
END
IF
THEN
ELSE
while
do
标识符
浮点常数
<
<=
=
<>
>
>=
+
-
*
/
:=
整形
浮点型
双精度
长整形
||,!,&&
//,/* */
字符串常量
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
WHILE
DO
ID
UCON
LT
LE
EQ
NE
GT
GE
PL
MI
MU
DI
IS
INT
FLOAT
DOUBLE
LONG
LOGIC
ZHISHI
STRING
主要函数:
函数
void scanner_example(FILE *fp)
int lookup(char p[])
void out(int a,char *p)
void report_error()
void main()
字母打头的字母数字
串
机内二进制表示
机内二进制表示
机内二进制表示
机内二进制表示
机内二进制表示
功能
扫描器函数
查找是否为关键字
输出
出现错误时强制退出
主函数
四.分析过程:
五.代码:
3
#include
#include
#include
#include
#define BEGIN 1
#define END
2
#define IF
#define THEN 4
#define ELSE 5
#define WHILE 6
#define DO
7
#define ID
8
#define UCON 9
#define LT
10
#define LE
11
#define EQ
12
#define NE
13
#define GT
14
#define GE
15
#define PL
16
#define MI
17
#define MU
18
#define DI
19
#define IS
20
#define INT 21
#define FLOAT 22
#define DOUBLE 23
#define LONG
24
#define LOGIC 25
#define ZHUSHI 26
#define STRING 27
#define TOKEN_SIZE 64
#define TAB_SIZE
15
char TOKEN[TOKEN_SIZE];
char ch;
extern int lookup(char*);
extern void out(int,char*);
extern void report_error();
//信息表保留 7 个关键字
typedef struct
{
int ad;
char id[10];
}info_ele;
info_ele
Tab[TAB_SIZE]={{1,"begin"},{2,"end"},{3,"if"},{4,"then"},{5,"else"},{6,"while"},{7,"do"},{21,
"int"},{22,"float"},{23,"double"},{24,"long"},{27,"string"}};
void scanner_example(FILE *fp)//扫描器函数
{
int i,c;
ch=fgetc(fp);
if(ch==' '||ch=='\n');
else if(isalpha(ch))
{
//遇见空格、回车继续
//是否为字母
TOKEN[0]=ch;
i=1;
ch=fgetc(fp);
while(isalnum(ch)||(ch=='_'))
{
//为字母或数字或下划线时进行循环
TOKEN[i]=ch;
i++;
ch=fgetc(fp);
}
fseek(fp,-1,1);
TOKEN[i]='\0';
c=lookup(TOKEN);
if (c==0)
//非字母或数字,下划线时回退一个字符
//检查保留字表
out(ID,TOKEN);//调用输出函数 out()
else
out(c,TOKEN);
}
else if(isdigit(ch))
{
//是否为数字
int flag=0;
TOKEN[0]=ch;
i=1;
ch=fgetc(fp);
while(isdigit(ch)||(ch=='.'))
{
if(ch=='.')
flag++;
TOKEN[i]=ch;
i++;
ch=fgetc(fp);
}
TOKEN[i]='\0';
fseek(fp,-1,1);
if(flag==0)
//小数点个数为 0 为整形
out(INT,TOKEN);
if(flag==1)
//判断是否为浮点型
out(DOUBLE,TOKEN);
if(flag>1)
printf("数字出错!\n");
}
else if(ch=='"')
{
//是否为字符串常量
TOKEN[0]=ch;
i=1;
ch=fgetc(fp);
while(isalnum(ch))
{
TOKEN[i]=ch;
i++;
ch=fgetc(fp);
}
if(ch=='"')
{
TOKEN[i]=ch;
out(STRING,TOKEN);
}
else
{
printf("字符串常量格式错误!\n");
}
TOKEN[i+1]='\0';
}
else
{
switch(ch)
//判断运算符
case '=':out(EQ,"=");break;
case ':':ch=fgetc(fp);
if(ch=='=')
out(IS,":=");break;
case '>':ch=fgetc(fp);
if(ch=='=')out(GE,">=");
else
{
fseek(fp,-1,1);
out(GT,">");
}
break;
case '&':ch=fgetc(fp);
if(ch=='&')
out(LOGIC,"&&");
else{
fseek(fp,-1,1);
}
break;
case '|':ch=fgetc(fp);
if(ch=='|')
out(LOGIC,"||");
else{
fseek(fp,-1,1);
}
break;
case '!':out(LOGIC,"!");break;
case '<':ch=fgetc(fp);
if(ch=='=')
out(LE,"<=");
else if(ch=='>')
out(NE,"<>");
else{
fseek(fp,-1,1);
out(LT,"<");
}
break;
case '+':out(PL,"+");break;
case '-':out(MI,"-");break;
case '*':out(MU,"*");break;
case '/':
{
ch=fgetc(fp);
if(ch=='/')
{
扫描分析
//判断是否为注释或是除运算,注释内容不
//单行注释
while(ch!='\n')
ch=fgetc(fp);
out(ZHUSHI," ");
}
else if(ch=='*')
//多行注释
{
}
else
{
}
char a,d;
d=fgetc(fp);
a=fgetc(fp);
while(a!=EOF&&(a!='/'||d!='*'))
{
d=a;
a=getc(fp);
}
if(a=='/'&&d=='*')
out(ZHUSHI," ");
else
{
}
printf("注释格式错误!\n");
fseek(fp,-1,1);
out(DI,"/");
}
break;
default:
break;
}
}
int lookup(char p[])
{
int i=0;
for(i;i