一、实验原理
1.1实验内容
通过本实验,应达到以下目标:
1.掌握从源程序文件中读取有效字符的方法和产生源程序的内部表示文件的方法。
2.掌握词法分析的实现方法。
3.上机调试编出的词法分析程序。
1.2实验内容
词法分析是作为相对独立的阶段来完成的(对源程序或中间结果从头到尾扫描一次,并作相应的加工处理,生成新的中间结果或目标程序)。在词法分析过程中,编译程序是通过操作系统从外部介质中读取源程序文件中的各个字符的。同时,为正确地识别单词,有时还需进行超前搜紫和回退字符等操作。因此,为了提高读盘效率和便于扫描器进行工作,通常可采用缓冲输入的方案,即在内存中设置一个适当大小的输入缓冲区,让操作系统直接将磁盘上的源程序字符串分批送入此缓冲区中,供扫描器进行处理。
1.3设计过程:
设计函数如下:
int isNum( char c)
//判断接收的字符是否是数字
int isLetter(char c)
//判断接收的字符是否是字母
char getLetter(char *s)
//读取指针所指的字符内容
void Print(char *s,int d)
//打印输出信息
void Judge(char *s)
//实现有限自动机的功能
二、实验目的
通过设计编制调试一个具体的词法分析程序,加深对词法分析原理的理解。并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法。
编制一个读单词过程,从输入的源程序中,识别出各个具有独立意义的单词,即基本保留字、标识符、常数、运算符、分隔符五大类。并依次输出各个单词的内部编码及单词符号自身值。
三、实验步骤
#include<string.h>
#include<stdio.h>
#define MAX 22 /*分析表的最大容量*/
#define RES_MAX 10 /*关键字的最大长度*/
#define MAXBUF 255 /*缓冲区的大小*/
char ch = ' '; /*存放读入当前的输入字符*/
int Line_NO; /*纪录行号*/
struct keywords { /*关键字*/
char lexptr[MAXBUF];
int token;
};
struct keywords symtable[MAX];
char str[MAX][10] = {"int", "char", "float", "main", "double", "case", "for", "if", "else", "do", "while", "void", "static", "return", "break", "struct", "const", "switch", "typedef"};
void init() {
int j;
for (j = 0; j < MAX; j++) {
strcpy(symtable[j].lexptr, str[j]);//把右边的复制给左边
symtable[j].token = j + 3;//最小的token是int,最大的token是typedef
}
}
/***************对关键字进行搜索**************/
int Iskeyword(char * is_res) {
int i;
for (i = 0; i < MAX; i++) {
if ((strcmp(symtable[i].lexptr, is_res)) == 0) break;
}
if (i < MAX) return symtable[i].token;
else return 0;
}
/*****************判断是否为字母*****************/
int IsLetter(char c) {
if (((c <= 'z') && (c >= 'a')) || ((c <= 'Z') && (c >= 'A'))) return 1;
else return 0;
}
/*************判断是否为数字**************/
int IsDigit(char c) {
if (c >= '0' && c <= '9') return 1;
else return 0;
}
/***************分析程序**************/
void analyse(FILE *fpin, FILE *fpout) {
/* 输入缓冲区,存放一个单词符号 */
char arr[MAXBUF];
int j = 0;
while ((ch = fgetc(fpin)) != EOF) {
/*碰到空格、tab则跳过*/
if (ch == ' ' || ch == '\t') {}
else if (ch == '\n') {
Line_NO++;
}
/*********************字符串的处理*************************/
else if (IsLetter(ch)) {
while (IsLetter(ch) | IsDigit(ch) | ch == '_') {
if ((ch <= 'Z') && (ch >= 'A'))
ch = ch + 32; /*忽略大小写,转化成小写*/
arr[j] = ch;
j++;
ch = fgetc(fpin);
}
/*输入指针回退一个字符*/
fseek(fpin, -1L, SEEK_CUR);//后退一个字节; 设置文件指针fpin的位置
arr[j] = '\0';
j = 0;
if (Iskeyword(arr)) { /*如果是关键字*/
fprintf(fpout, "%s\t\t%d\t\t关键字\n", arr, Iskeyword(arr));
} else
fprintf(fpout, "%s\t\t%d\t\t标识符\n", arr, 1); /*普通标识符*/
/*************************数字的处理****************************/
} else if (IsDigit(ch)) {
int s = 0;
while (IsDigit(ch) | IsLetter(ch)) {
if (IsLetter(ch)) {
arr[j] = ch;
j++;
ch = fgetc(fpin);
s = 1;
} else if (IsDigit(ch)) {
arr[j] = ch;
j++;
ch = fgetc(fpin);
}
}
fseek(fpin, -1L, SEEK_CUR);
arr[j] = '\0';
j = 0;
if (s == 0)
fprintf(fpout, "%s\t\t%d\t\t无符号整数\n", arr, 2) ;
else if (s == 1)
fprintf(fpout, "%s\t\t%d\t\t错误\n", arr, 3) ;
} else switch (ch) {
case'+' :
fprintf(fpout, "%s\t\t%d\t\t运算符\n", "+", 41);
break;
case'-' :
fprintf(fpout, "%s\t\t%d\t\t运算符\n", "-", 42);
break;
case'*' :
fprintf(fpout, "%s\t\t%d\t\t运算符\n", "*", 43);
break;
case'(' :
fprintf(fpout, "%s\t\t%d\t\t分界符\n", "(", 25);
break;
case')' :
fprintf(fpout, "%s\t\t%d\t\t分界符\n", ")", 26);
break;
case'[' :
fprintf(fpout, "%s\t\t%d\t\t分界符\n", "[", 27);
break;
case']' :
fprintf(fpout, "%s\t\t%d\t\t分界符\n", "]", 28);
break;
case';' :
fprintf(fpout, "%s\t\t%d\t\t分界符\n", ";", 29);
break;
case'=' :
fprintf(fpout, "%s\t\t%d\t\t运算符\n", "=", 45);
break;
case'.' :
fprintf(fpout, "%s\t\t%d\t\t分界符\n", ".", 30);
break;
case',' :
fprintf(fpout, "%s\t\t%d\t\t分界符\n", ",", 31);
break;
case':' :
fprintf(fpout, "%s\t\t%d\t\t分界符\n", ":", 32);
break;
case'{' :
fprintf(fpout, "%s\t\t%d\t\t分界符\n", "{", 33);
break;
case'}' :
fprintf(fpout, "%s\t\t%d\t\t分界符\n", "}", 34);
break;
case'>' : {
ch = fgetc(fpin);
if (ch == '=')
fprintf(fpout, "%s\t\t%d\t\t运算符\n", ">=", 46);
else {
fprintf(fpout, "%s\t\t%d\t\t运算符\n", ">", 47);
fseek(fpin, -1L, SEEK_CUR);
}
}
break;
case'<' : {
ch = fgetc(fpin);
if (ch == '=')
fprintf(fpout, "%s\t\t%d\t\t运算符\n", "<=", 48);
else if (ch == '>')
fprintf(fpout, "%s\t\t%d\n", "<>", 50);
else {
fprintf(fpout, "%s\t\t%d\t\t运算符\n", "<", 49);
fseek(fpin, -1L, SEEK_CUR);
}
}
break;
/***************出现在/ /之间的全部作为注释部分处理*******************/
case'/' : {
ch = fgetc(fpin);
if (ch == '*') {
while (ch != '/' && ch != EOF) {
ch = fgetc(fpin);
}
if (ch == EOF)
fprintf(fpout, "缺少一个'/'");
} else {
fprintf(fpout, "%s\t\t%d\t\t运算符\n", "/", 44);
fseek(fpin, -1L, SEEK_CUR);
}
}
break;
/***************非法字符*******************/
default :
fprintf(fpout, "在第%d行无法识别的字符\t%c\n", Line_NO, ch);
}
}
}
int main() {
char in_fn[25], out_fn[25];
FILE * fpin, * fpout;
printf("输入分析目标文件名:\n");
scanf("%s", in_fn);
printf("输入要保存分析的目标文件名:\n");
scanf("%s", out_fn);
printf("保存成功!\n");
fpin = fopen(in_fn, "r");
fpout = fopen(out_fn, "w");
init();
analyse(fpin, fpout);
fclose(fpin);
fclose(fpout);
return 0;
}