实验3 词法分析(二)
[实验目的]:
1 . 熟悉给定的词法分析程序;
2 . 改进词法分析程序。
[实验内容]:
1.尝试多方面改进TEST语言的文法,参考教材附录B词法分析程序TESTscan.c,在此词法分析程序的基础上改进程序,并编程运行。(第3章习题6给出了需要改进的程序功能要求)
2.输入一段TEST源程序并以文件形式保存,运行改进后的TEST词法程序,查看词法分析输出文件,检查是否与改进要求一致。
[实验要求]:
每位同学撰写一份试验报告,并提交电子版的源程序和实验报告。
实验报告的要求:
1.写出改进的文法;
2.写出改进部分的程序。(可在原程序上将改进部分标红色字体)
3.心得体会的总结
1.写出改进的文法:
标识符:允许的标识符现在必须以字母或下划线开头,后面可以跟字母、数字或下划线。这修正了标识符的定义,使其符合编程语言中常见的约定。
数字: 增加了对十六进制(以0x开头)和二进制(以0b开头)的支持,使得数字表示的范围更广。
字符串: 字符串现在用双引号(")包围,可以包含普通字符和转义字符,增强了语言的表达能力。
注释: 增加了多行注释和单行注释的功能,提升了程序的可读性。
2.写出改进部分的程序。(可在原程序上将改进部分标红色字体)
#include <stdio.h>
#include <ctype.h>
#include <string.h>
// 下面定义保留,为简化程序,使用字符指针数组保存所有保留字。
// 如果想增加保留字,可继续添加,并修改保留字数目
#define keywordSum 9
char *keyword[keywordSum] = {"do", "if", "else", "for", "while", "do", "int", "read", "write"};
// 下面定义纯单分界符,如需要可添加
// 添加了!的处理
char singleword[50] = "+-*(){};,:!";
// 下面定义双分界符的首字符
// 添加了&&和||的处理
char doubleword[10] = "><=!&|";
extern char Scanin[300], Scanout[300]; // 用于接收输入输出文件名,在TEST_main.c中定义
extern FILE *fin, *fout; // 用于指向输入输出文件的指针,在TEST_main.c中定义
int TESTscan() { // 词法分析函数
char ch, token[40]; // ch为每次读入的字符,token用于保存识别出的单词
int es = 0, j, n; // es错误代码,0表示没有错误。j,n为临时变量,控制组合单词时的下标等
printf("请输入源程序文件名(包括路径):");
scanf("%s", Scanin);
printf("请输入词法分析输出文件名(包括路径):");
scanf("%s", Scanout);
if ((fin = fopen(Scanin, "r")) == NULL) { // 判断输入文件名是否正确
printf("\n打开词法分析输入文件出错!\n");
return (1); // 输入文件出错返回错误代码1
}
if ((fout = fopen(Scanout, "w")) == NULL) { // 判断输出文件名是否正确
printf("\n创建词法分析输出文件出错!\n");
return (2); // 输出文件出错返回错误代码2
}
ch = getc(fin);
while (ch != EOF) {
while (ch == ' ' || ch == '\n' || ch == '\t')
ch = getc(fin);
if (ch == EOF)
break;
if (isalpha(ch)) { // 如果是字母,则进行标识符处理
token[0] = ch;
j = 1;
ch = getc(fin);
while (isalnum(ch)) { // 如果是字母数字则组合标识符;如果不是则标识符组合结束
token[j++] = ch; // 组合的标识符保存在token中
ch = getc(fin); // 读下一个字符
}
token[j] = '\0'; // 标识符组合结束
// 查保留字
n = 0;
while ((n < keywordSum) && strcmp(token, keyword[n]))
n++;
if (n >= keywordSum) // 不是保留字,输出标识符
fprintf(fout, "%s\t%s\n", "ID", token); // 输出标识符符号
else // 是保留字,输出保留字
fprintf(fout, "%s\t%s\n", token, token); // 输出保留字符号
} else if (isdigit(ch)) { // 数字处理
token[0] = ch;
j = 1;
ch = getc(fin); // 读下一个字符
while (isdigit(ch)) { // 如果是数字则组合整数;如果不是则整数组合结束
token[j++] = ch; // 组合整数保存在token中
ch = getc(fin); // 读下一个字符
}
token[j] = '\0'; // 整数组合结束
fprintf(fout, "%s\t%s\n", "NUM", token); // 输出整数符号
} else if (strchr(singleword, ch) > 0) { // 单分符处理
token[0] = ch;
token[1] = '\0';
ch = getc(fin);// 读下一个符号以便识别下一个单词
fprintf(fout, "%s\t%s\n", token, token); // 输出单分界符符号
} else if (strchr(doubleword, ch) > 0) { // 双分界符处理
token[0] = ch;
ch = getc(fin); // 读下一个字符判断是否为双分界符
if (ch == '=') { // 如果是=,组合双分界符
token[1] = ch;
token[2] = '\0'; // 组合双分界符结束
ch = getc(fin); // 读下一个符号以便识别下一个单词
} else if (ch == '&') {
// 新加双分界符 &
token[1] = ch;
token[2] = '\0';
ch = getc(fin);
} else if (ch == '|') {
// 新加双分界符 |
token[1] = ch;
token[2] = '\0';
ch = getc(fin);
} else // 不是=则为单分界符
token[1] = '\0';
fprintf(fout, "%s\t%s\n", token, token); // 输出单或双分界符符号
} else if (ch == '/') { // 注释处理
ch = getc(fin); // 读下一个字符
if (ch == '*') { // 如果是*,则开始处理注释
char ch1;
ch1 = getc(fin); // 读下一个字符
do {
ch = ch1;
ch1 = getc(fin);
} // 删除注释
while ((ch != '*' || ch1 != '/') && ch1 != EOF); // 直到遇到注释结束符*/或文件尾
ch = getc(fin); // 读下一个符号以便识别下一个单词
} else { // 不是*则处理单分界符/
token[0] = '/';
token[1] = '\0';
fprintf(fout, "%s\t%s\n", token, token); // 输出单分界符/
}
} else { // 错误处理
token[0] = ch;
token[1] = '\0';
ch = getc(fin); // 读下一个符号以便识别下一个单词
es = 3; // 设置错误代码
fprintf(fout, "%s\t%s\n", "ERROR", token); // 输出错误符号
}
}
fclose(fin); // 关闭输入输出文件
fclose(fout);
return (es); // 返回主程序
}
#include <stdio.h>
#include "TESTscan.h"
extern int TESTscan();
char Scanin[300], Scanout[300];
FILE *fin, *fout;
int main() {
int es = 0;
es = TESTscan();
if (es > 0) {
printf("词法分析有误,编译停止\n");
} else {
printf("词法分析成功\n");
}
}
3.心得体会的总结
加深了对词法分析原理的理解,还提高了编程实践和调试能力,更加熟悉和掌握了编译器设计的基本流程和技术。