MIDL词法分析程序(C++)

一、MIDL的词法规则

1. 语言的关键字(16个):

structfloatbooleanshortlong
doubleint8int16int32int64
uint8uint16uint32uint64char
unsigned

注:关键字是保留字,并且不区分大小写。

2. 语言的专用符号17个:

{};[]
*+-~/
%>><<&^
|,

3. 标示符ID、整数INTEGER、 字符串STRING和BOOLEAN等4个词法规则(标黑的)通过下列正则表达式定义:

ID = LETTER (UNDERLINE?( LETTER | DIGIT))*
LETTER = [a-z] | [A- Z]
DIGIT = [0-9]
UNDERLINE= _
INTEGER = (0 | [1-9] [0-9]*) INTEGER_TYPE_SUFFIX?
INTEGER_TYPE_SUFFIX = l | L
STRING = " ( ~\ | ~" )* "
BOOLEAN = TRUE | FALSE

4. 补充说明的注意事项

其中,正则符号标识含义如下表:

~\ | ~"表示匹配除了 \ ,"外所有字符
[a-z]表示a字符到z字符,与课件ppt中含义一致
?表示匹配 0 或 1 次,与课件ppt中含义一致
+表示匹配 1 或 多次,与课件ppt中含义一致
*表示匹配 0 或 多次,与课件ppt中含义一致
a | b表示匹配 a字符或b字符,与课件ppt中含义一致
( )表示一个组,比如()*表示*对括号内组整体起作用,与课件ppt中含义一致

空白、换行符和制表符在词法分析时可以忽略掉,但是字符串本身的空白、换行符和制表符不能忽略

二、词法分析程序课程设计要求

词法分析程序的设计与实现需要按照以下要求完成。

  1. 基于词法规则设计词法分析器(20分)
    画出确定的有穷自动机(确定化),并提供必要的文字说明。提交状态转换图.doc
  2. 词法分析程序的编程实现与测试(80分)
    1. 编程实现词法分析器,提交可执行词法分析程序的源程序**(语言不限,60分)**
      编程实现词法分析程序,该词法分析程序能够读取测试输入文件中的源程序,并将其词法分析的结果即token序列或词法分析的所有错误信息输出到tokenOut.txt中,以便于检查你的词法分析程序的正确性。输出的错误信息要包括错误所在行数,错误类型等信息(同目录下有测试文件夹,共五大类测试输入)。
    2. 提交所生成的词法分析程序的测试方案(20分).doc

注:将状态转换图.doc、源程序、以及词法分析程序测试方案.doc文件放在一个文件夹下,按照“班级_学号_姓名”的命名方式打包提交。如果还有其它需要说明的问题须写在readme.doc中。

三、有关附加分的说明

关于字符串,如果词法分析程序考虑转义字符(正规表达式的定义如下),则作为加分项目,可以选做。即如果词法分析程序考虑了对转义字符的支持,额外加分10分,但总分不得超过100分。例如,根据词法分析程序的完成情况,某同学取得92分的成绩,但由于该同学的词法分析程序正确处理了转义字符(必须在状态转换图、词法分析源程序和readme.doc中分别进行备注),该同学的总分合计为100分。

STRING = " (ESCAPE_SEQUENCE | (~\ | ~") )* "
ESCAPE_SEQUENCE = \ ( b | t | n | f | r | " | \ )

四、确定的有穷自动机

在这里插入图片描述

五、MIDL词法分析程序(C++)

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <iomanip>

using namespace std;

//设置保留字,不区分大小写!!
string keyWord[] = { "struct","float","boolean",
"short","long","double","int8","int16","int32",
"int64","uint8","uint16","uint32","uint64","char",
"unsigned" };

char separator[] = {'{', '}', ';', '(', ')','[',']', ','}; //分隔符
char singleOperator[] = {'*', '+', '-', '~', '/', '%','&','^', '|'}; //单目运算符
string doubleOperator[] = { ">>","<<" }; //双目运算符
char filter[] = {' ','\t','\r','\n'}; //过滤符
int lineno = 1; //记录当前字符所在行数

/**判断是否为过滤符**/
bool isFilter(char ch) {
	for (int i = 0; i < strlen(filter); i++) {
		if (ch == filter[i]) {
			return true;
		}
	}
	return false;
}

/**判断是否为关键字**/
bool isKeyWord(string word) {
	//将单词中的大写字母转换为小写字母
	for (int i = 0; i < word.length(); i++) {
		if (word[i] >= 'A' && word[i] <= 'Z') {
			word[i] += 'a' - 'A';
		}
	}
	//cout << word << endl;
	for (int i = 0; i < 16; i++) {
		if (keyWord[i] == word) {
			return true;
		}
	}
	return false;
}

/**判断是否为字母**/
bool isLetter(char ch) {
	if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
		return true;
	else
		return false;
}

/**判断是否为数字**/
bool isDigit(char ch) {
	if (ch >= '0' && ch <= '9')
		return true;
	else
		return false;
}

/**判断是否为BOOLEAN类型**/
bool isBoolean(string word) {
	if (word == "TRUE" || word == "FALSE") {
		return true;
	}
	else {
		return false;
	}
}

/**判断是否为单目运算符**/
bool isSingleOperator(char ch) {
	switch (ch) {
		case '*':
			return true;
		case '+':
			return true;
		case '-':
			return true;
		case '~':
			return true;
		case '/':
			return true;
		case '%':
			return true;
		case '&':
			return true;
		case '^':
			return true;
		case '|':
			return true;
	}
	return false;
}

/**判断是否为分隔符**/
bool isSeparator(char ch) {
	switch (ch) {
		case '{':
			return true;
		case '}':
			return true;
		case ';':
			return true;
		case '(':
			return true;
		case ')':
			return true;
		case '[':
			return true;
		case ']':
			return true;
		case ',':
			return true;
	}
	return false;
}

void analyse(string line,string writefilename) {
	ofstream out(writefilename, ios::app); //app表示每次都定位到文件末尾
	if (out.fail()) {
		cout << "写入文件打开失败!" << endl;
	}
	//line += "";
	int i = 0;
	char ch = line[i];
	string arr = "";
	i = 1;

	int outlen = 40;
	while (i <= line.length()) {
		//ch = line[i];
		arr = "";
		if (isFilter(ch)) { //判断是否为过滤符
			ch = line[i++];
		}
		else if (ch == '_') {
			while (isLetter(ch) || isDigit(ch) || ch == '_') { //如果字符是字母、数字或下划线
				arr += ch;
				ch = line[i++]; //读取下一个字符
			}
			out << endl << "第" << lineno << "行:  " << arr << "\t非法的标识符ID!" << endl << endl; //打印错误信息
		}
		else if(isLetter(ch)){//如果单词的首字符是字母
			while (isLetter(ch)||isDigit(ch)||ch=='_') { //如果字符是字母、数字或下划线
				arr += ch;
				ch = line[i++]; //读取下一个字符
			}
			if (isKeyWord(arr)) {
				//token_type = 1; //单词类型为关键字
				out<< arr << setw(outlen -arr.length())<< "关键字" << endl;
			}
			else if(isBoolean(arr)){
				out<<arr<< setw(outlen - arr.length())<<"BOOLEAN"<<endl;
			}
			else {
				if (arr[arr.length() - 1] != '_') {//如果末尾不是下划线
					out << arr << setw(outlen - arr.length())<<"标识符ID" << endl;
				}
				else {
					out <<endl<< "第" << lineno << "行:  "<<arr<<"\t标识符末尾不能是下划线!" <<endl<<endl; //打印错误信息
					//break;
				}
			}

		}
		else if (isDigit(ch)) { //判断是否为数字
			while (isDigit(ch)) {
				arr += ch;
				ch = line[i++]; //读取下一个字符
			}
			if (ch == 'l' || ch == 'L') { //如果末尾有l或L
				arr += ch;
				ch = line[i++]; //读取下一个字符
				if (arr.length() > 2 && arr[0] == '0') {//判断第一个数字是否为0				
					out <<endl<< "第" << lineno << "行:  "<<arr<<"第一个数字不能为零!" << endl<<endl; //打印错误信息
					//break;
				}
			}
			//如果末尾没有l或L
			else if (arr.length() > 1 && arr[0] == '0') {//判断第一个数字是否为0				
				out <<endl<< "第" << lineno << "行:  "<<arr<<"第一个数字不能为零!" << endl<<endl; //打印错误信息
				//break;
			}
			else {
				out << arr << setw(outlen - arr.length())<<"整数INTEGER" << endl;
			}
		}
		else if (isSingleOperator(ch)) {
			arr += ch;
			ch = line[i++];
			out << arr << setw(outlen - arr.length())<< "单目运算符" << endl;
		}
		else if (isSeparator(ch)) {
			arr += ch;
			ch = line[i++];
			out << arr << setw(outlen - arr.length())<<"分隔符" << endl;
		}
		else if (ch == '>') { //判断是否为双目运算符>>
			arr += ch;
			ch = line[i++]; //读取下一个字符
			if (ch == '>') {
				arr += ch;
				ch = line[i++]; //读取下一个字符
				out << arr << setw(outlen - arr.length())<<"双目运算符" << endl;
			}
			else {
				out <<endl<< "第" << lineno << "行:  "<<arr<<"\t为非法符号!" << endl<<endl; //打印错误信息
				//break;
			}
		}
		else if (ch == '<') { //判断是否为双目运算符<<
			arr += ch;
			ch = line[i++]; //读取下一个字符
			if (ch == '<') {
				arr += ch;
				ch = line[i++]; //读取下一个字符
				out << arr << setw(outlen - arr.length())<<"双目运算符" << endl;
			}
			else {
				out << endl << "第" << lineno << "行:  " << arr << "\t为非法字符!" << endl << endl; //打印错误信息
				//break;
			}
		}
		else if (ch == '\"') { //判断是否为字符串
			arr += ch;
			char bfch = ch;
			ch = line[i++];
			bool flag = 1; //是否合法
			while (i <= line.length() && !(ch == '\"' && bfch !='\\')){
				/*
				* 考虑了转义字符!!!
				*/
				if (ch == '\\') {
					arr += '\\';
					bfch = ch;
					ch = line[i++];
					if (ch == 'b' || ch == 't' || ch == 'n' || ch == 'f' || ch == 'r') {
						arr += ch;
						bfch = ch;
						ch = line[i++];
					}
					else if (ch == '\"' ) {
						arr += '\"';
						bfch = ch;
						ch = line[i++];
					}
					else if (ch == '\\') {
						arr += '\\';
						bfch = ch;
						ch = line[i++];
					}
					else {
						flag = 0; //字符串不合法
					}		
				}
				else {
					arr += ch;
					bfch = ch;
					ch = line[i++];
				}
				
			}
			arr += ch;
			ch = line[i++];
			
			if (flag) {
				//printf("%s\t\t字符串STRING\n", arr.c_str());
				out <<arr<< setw(outlen - arr.length())<<"字符串STRING"<< endl;
			}
			else {
				out << endl << "第" << lineno << "行:   \t字符串"<<arr<<"中存在非法字符!" << endl << endl; //打印错误
			}
		}
		else{
			out <<endl<< "第" << lineno << "行:\t"<<ch<<"\t无法识别的字符!" <<endl<< endl; //打印错误信息
			break;
		}
	}
	out.close(); //关闭文件输出流
}

void run(string filename,string writefile) {
	ofstream out(writefile, ios::trunc); //清空文件内容
	out.close(); //关闭文件输出流

	ifstream in(filename);
	string line = "";
	if (in) {//如果有该文件
		lineno = 1; //记录当前字符所在行数
		cout << "=====================MIDL词法分析程序开始===================" << endl<<endl;
		cout<<"正在分析\""<<filename<<"\"文件"<<endl<<endl;
		while (getline(in, line)) { //读取一行
			cout << "正在分析第"<<lineno<<"行……" << endl;
			analyse(line,writefile); //对这一行进行词法分析
			lineno++; //所在行数加1
		}
		cout << endl<<"解析结果已写入\""<<writefile<<"\"文件中!" << endl<<endl;
		cout << "=====================MIDL词法分析程序结束===================" << endl<<endl<<endl<<endl;
	}
	else { //如果没有该文件,则打印报错
		cout << "文件不存在!" << endl;
	}
	in.close();
}

int main() {

	//此处可以更改源程序文件
	string read[] = {"test_1_kw.txt","test_2_kw.txt","test_3_formula.txt","test_4_identifier.txt",
		"test_5_identifier_with_error.txt","test_6_identifier_with_error.txt",
		"test_7_expression.txt","test_8_complex.txt","test_9_complex_with_escape.txt"};
	
	//此处可以修改分析后生成的文件名
	string write[] = { "tokenOut1.txt" ,"tokenOut2.txt" ,"tokenOut3.txt" ,"tokenOut4.txt" ,
		"tokenOut5.txt","tokenOut6.txt","tokenOut7.txt","tokenOut8.txt","tokenOut9.txt" };

	for (int i = 0; i < 9; i++) { //遍历测试所有文件
		run(read[i],write[i]);
	}

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一匹好人呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值