编译原理 实验2

写在前面

纯粹,为了记录.
以后某一天,回过头来看看.给大学留些痕迹hh.
PS:很多思路也都是学习前辈们的,如果能顺便帮到你,不客气!

内容

编译原理 实验2
通过编制调试一个具体的词法分析程序,加深对词法分析原理的理解。并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法。

本实验以用户指定的想编译的以C语言编写的文件作为词法分析程序的输入数据。

在进行词法分析中,先自文件头开始以行为单位扫描程序,将该行的字符读入预先设定的一个数组缓冲区中,然后对该数组的字符逐词分割,进行词法分析,将每个词分割成关键字、标识符、常量和运算符四种词种,最终产生四个相对应的表,即关键字表、标识符表、常量表和运算符表,它们以文件的形式进行存储。除此之外,还产生一个编译后的文件,它指定了每个词在四个表中的位置。

编程实现源程序的输入和扫描程序。

源代码

#include <iostream>
#include <string>
#include <fstream>	//ifstream读文件,ofstream写文件,fstream读写文件
#include <windows.h>
using namespace std;

/************************************全局常量************************************/
//C语言关键字,32个
const string keyWord[32] = { "auto", "double", "int", "struct", "break", "else", "long", "switch",

                             "case", "enum", "register", "typedef", "char", "extern", "return", "union",

                             "const", "float" "short", "unsigned", "continue", "for", "signed", "void",

                             "default", "goto", "sizeof", "volatile", "do", "if", "while", "static"
                           };
const string IN_FILE_PATH = "1.txt";
const string OUT_FILE_PATH = "after_com.txt";
const string KEY_FILE_PATH = "key.txt";
const string OPERATION_FILE_PATH = "operation.txt";
const string ID_FILE_PATH = "id.txt";
const string CONST_FILE_PATH = "const.txt";
/************************************全局变量************************************/
int keyPos = 0;		//记录关键字输出表的行位置
int operationPos = 0;	//记录运算符输出表的行位置
int idPos = 0;		//记录标识符输出表的行位置
int constPos = 0;	//记录常量输出表的行位置

/************************************文件输入输出流************************************/
//ios::app——所有输出附加在文件末尾
ifstream inStream(IN_FILE_PATH, ios::in);			//源代码
ofstream outStream(OUT_FILE_PATH, ios::out);		//编译后的文件
ofstream keyStream(KEY_FILE_PATH, ios::out);		//关键字文件
ofstream idStream(ID_FILE_PATH, ios::out);			//标识符文件
ofstream operationStream(OPERATION_FILE_PATH, ios::out);//运算符文件
ofstream constStream(CONST_FILE_PATH, ios::out);	//常量文件

/************************************函数声明************************************/
string UTF8ToGB(const char *str);	/*转换编码函数*/
bool isLetter(char ch);	/*判断字符是否为字母*/
bool isDigit(char ch);	/*判断字符是否为数字*/
void lexicalAnalyzer(string line, int row);	/*词法分析器*/
bool isKeyWord(string word);	/*判断是否是关键字*/
bool openAllFile();		/*打开所需的所有文件:1个输入文件即源代码、5个输出文件*/
void closeAllFile();	/*关闭所有文件*/


int main() {

	if (!openAllFile()) {
		cout << "存在文件未成功打开!" << endl;
		return -1;
	}

	cout << "##############################################################" << endl;
	cout << "###	词法分析结果如下,数据已输出至 当前目录下:	    ##" << endl;
	cout << "###	关键字表、标识符表、常量表、运算符表、编译后的文件. ##" << endl;
	cout << "##############################################################" << endl;
	cout << endl;

	//逐行读取源代码文件
	int row_index = 0;
	while (!inStream.eof()) {
		string lineBuf;	//存放读取到的一行字符串
		getline(inStream, lineBuf, '\n');	//注意,'\n'截止字符是从流中取出来丢弃

		lineBuf = UTF8ToGB(lineBuf.c_str()).c_str();	//转换编码,解决中文乱码的问题

		lexicalAnalyzer(lineBuf, row_index);
		row_index ++ ;
		//cout << lineBuf << endl;		//打印到屏幕
		//outStream << lineBuf << endl;	//输出到文件2.txt
	}
	closeAllFile();
	return 0;
}

/********************************************
* 功能:对输入的一行源代码进行词法分析,并将结果输出到对应文件
* 输入参数:string line,int row(源代码行号)
* 返回类型:无
* 说明:将每个词划分为关键字、标识符、常量和运算符四种词种,其余词归为其他词种
* 注:
	1、数字未加入浮点类型的状态转换,只分割为整型数;
	2、标识符可以字母或者下划线开头;
	3、运算符只判断简单的几个运算符;
*********************************************/
void lexicalAnalyzer(string line, int row) {
	int index = 0;	//当前指针的位置,即列号
	int len = line.size();
	//去掉头部空白
	//即从开始态进入0态(依据状态图)
	while (index <= len - 1 && line[index] == ' ') {
		index ++ ;
	}

	while (index <= len - 1) {

		char ch = line[index];	//读取当前位置的字符
		string word = "";	//存放中间态、终态的单词

		/*
		当前为开始态,即0态,word为空
		*/

		//字符为字母或者下划线
		if (isLetter(ch) || ch == '_') {
			word += ch; //由0态变为1态
			ch = line[ ++ index];

			//字符为字母或者下划线或者数字
			while (isLetter(ch) || ch == '_' || isDigit(ch)) {
				word += ch;	//由1态变为1态,循环
				ch = line[ ++ index];
			}
			//从循环出来,即 非字母、下划线、数字,则由1态进入2态
			//cout << "关键字或者标识符:" << word << endl;
			//注意index此时不后移,因为未存入word

			if (isKeyWord(word)) {
				cout << "关键字:" << word << "  位置:(" << row << "," << index - word.size() << ")" << endl;

				//写入关键字表:二元组(关键字,在源代码中的起始位置)
				keyStream << word << "  在源代码中的起始位置:(" << row << "," << index - word.size() << ")" << endl;
				//写入编译后的表:三元组(关键字,表名,在表中的起始位置)
				outStream << word << "  表名:key.txt" << "  在表中的起始位置:(" << keyPos << ",0)" << endl;
				keyPos ++ ;

			} else {
				cout << "标识符:" << word << "  位置:(" << row << "," << index - word.size() << ")" << endl;

				//写入标识符表:二元组(标识符,在源代码中的起始位置)
				idStream << word << "  在源代码中的起始位置:(" << row << "," << index - word.size() << ")" << endl;
				//写入编译后的表:三元组(标识符,表名,在表中的起始位置)
				outStream << word << "  表名:id.txt" << "  在表中的起始位置:(" << idPos << ",0)" << endl;
				idPos ++ ;
			}
		}

		//字符为数字
		else if (isDigit(ch)) {
			//字符为数字,循环从3态变为3态
			while (isDigit(ch)) {
				word += ch;
				ch = line[ ++ index];
			}
			//字符为非数字,则由3态变为4态
			cout << "标识符:" << word << "  位置:(" << row << "," << index - word.size() << ")" << endl;

			//写入常量表:二元组(常量,在源代码中的起始位置)
			constStream << word << "  在源代码中的起始位置:(" << row << "," << index - word.size() << ")" << endl;
			//写入编译后的表:三元组(常量,表名,在表中的起始位置)
			outStream << word << "  表名:const.txt" << "  在表中的起始位置:(" << constPos << ",0)" << endl;
			constPos ++ ;


			//index也不移动,因为该index位置字符未使用,同上
		}

		//字符为算术运算符
		else if (ch == '+' || ch == '-' || ch == '*' || ch == '/') {
			//则由0态变为5、6、7、8、9态其中一个
			word += ch;
			index ++ ;	//指针后移
			cout << "算术运算符:" << word << "  位置:(" << row << "," << index - word.size() << ")" << endl;

			//写入算术运算符表:二元组(运算符,在源代码中的起始位置)
			operationStream << word << "  在源代码中的起始位置:(" << row << "," << index - word.size() << ")" << endl;
			//写入编译后的表:三元组(运算符,表名,在表中的起始位置)
			outStream << word << "  表名:operation.txt" << "  在表中的起始位置:(" << operationPos << ",0)" << endl;
			operationPos ++ ;
		}

		//字符为比较运算符
		else if (ch == '<' || ch == '>' || ch == '=') {
			//由0态变为10态
			word += ch;
			ch = line[ ++ index];
			if (ch == '=') {
				word += ch;	//由10态变为11态
				index ++ ;	//该位置字符已用,指针后移
			}
			cout << "比较运算符:" << word << "  位置:(" << row << "," << index - word.size() << ")" << endl;

			//写入运算符表:二元组(运算符,在源代码中的起始位置)
			operationStream << word << "  在源代码中的起始位置:(" << row << "," << index - word.size() << ")" << endl;
			//写入编译后的表:三元组(运算符,表名,在表中的起始位置)
			outStream << word << "  表名:operation.txt" << "  在表中的起始位置:(" << operationPos << ",0)" << endl;
			operationPos ++ ;
		}

		//为其他字符
		else {
			index ++ ;	//指针后移
		}
	}


	index ++ ;	//指针后移,继续读取字符
}

/********************************************
* 功能:判断字符是否为字母
* 输入参数:字符ch
* 返回类型:bool
* 说明:是则返回true,不是返回false
*********************************************/
bool isLetter(char ch) {
	if ( (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
		return true;	//字母
	}
	return false;	//非字母
}

/********************************************
* 功能:判断字符是否为数字
* 输入参数:字符ch
* 返回类型:bool
* 说明:是则返回true,不是返回false
*********************************************/
bool isDigit(char ch) {
	if (ch >= '0' && ch <= '9') {
		return true;	//数字
	}
	return false;	//非数字
}

/********************************************
* 功能:判断单词是否为关键字
* 输入参数:string word
* 返回类型:bool
* 说明:在关键字常量数组中遍历,寻找与word匹配的关键字,找到则返回true,否则返回false
*********************************************/
bool isKeyWord(string word) {
	for (int i = 0; i < 32; i ++ ) {
		if (word == keyWord[i]) {
			return true;	//是关键字
		}
	}
	return false;	//非关键字
}

/********************************************
* 功能:判断5个文件是否全部正常打开
* 输入参数:无
* 返回类型:无
*********************************************/
void closeAllFile() {
	inStream.close();
	outStream.close();
	keyStream.close();
	idStream.close();
	operationStream.close();
	constStream.close();
}

/********************************************
* 功能:判断5个文件是否全部正常打开
* 输入参数:无
* 返回类型:bool
* 说明:成功则返回true,否则false
*********************************************/
bool openAllFile() {
	if (!inStream) {
		cout << "open in fail!" << endl;
		return false;
	}
	if (!outStream) {
		cout << "open out fail!" << endl;
		return false;
	}
	if (!keyStream) {
		cout << "open key fail!" << endl;
		return false;
	}
	if (!idStream) {
		cout << "open id fail!" << endl;
		return false;
	}
	if (!operationStream) {
		cout << "open operation fail!" << endl;
		return -1;
	}
	if (!constStream) {
		cout << "open const fail!" << endl;
		return false;
	}
	return true;	//全部文件打开成功
}

/*转换编码函数,解决中文乱码的问题*/
/*该函数引用自:作者——https://blog.csdn.net/qd1308504206*/
std::string UTF8ToGB(const char *str) {
	std::string result;
	WCHAR *strSrc;
	LPSTR szRes;

	//获得临时变量的大小
	int i = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
	strSrc = new WCHAR[i + 1];
	MultiByteToWideChar(CP_UTF8, 0, str, -1, strSrc, i);

	//获得临时变量的大小
	i = WideCharToMultiByte(CP_ACP, 0, strSrc, -1, NULL, 0, NULL, NULL);
	szRes = new CHAR[i + 1];
	WideCharToMultiByte(CP_ACP, 0, strSrc, -1, szRes, i, NULL, NULL);

	result = szRes;
	delete[]strSrc;
	delete[]szRes;

	return result;
}

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
贵州大学编译原理CSDN,可以理解为在贵州大学学习编译原理这门课程时,有关该课程的一些学习资源来源于CSDN网站。 编译原理是计算机科学与技术专业的核心课程之一,主要研究程序的编译过程和编译器的设计原理。在贵州大学学习编译原理这门课程时,CSDN作为一个知识分享的平台,可以为学生们提供丰富的学习资料和经验分享。学生们可以通过搜索CSDN网站上的编译原理相关内容,了解该课程的基本概念、原理和实践技巧。 CSDN上有很多关于编译原理的博客、问答和教程,这些资源可以帮助学生们深入理解课程的知识点和难点。学生们可以从CSDN上了解到编译原理的相关算法和数据结构,学习编译器的核心技术,如词法分析、语法分析和语义分析等。此外,CSDN上还有一些编译原理的案例分析和实践项目,可以帮助学生们将课程中的理论知识应用到实际编译器的设计与实现中。 通过CSDN,学生们还可以参与编译原理相关的技术讨论和交流。在CSDN论坛或博客评论区,学生们可以提出自己的问题、解答他人的疑惑,与其他编译原理爱好者一同交流学习经验和心得。这种交流可以促进学生们的思考和理解,加深对编译原理的认识与兴趣。 综上所述,贵州大学编译原理CSDN可以作为学生们学习编译原理课程的一个辅助资源。通过参考CSDN上的学习资料和参与技术交流,学生们可以更加全面地掌握编译原理的知识与技能,为未来的学习和工作打下扎实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值