词法分析器C++实现

词法分析器C++实现


单词分类表
大概可以分为以下几个大类:
1.标识符 有程序设计者自己定义但是必须符合标识符的定义规范
2.关键字
3.常量
4.运算符
5.分界符

单词符号 种类 类别号 单词符号 种类 类别号
int 关键字 1 + 运算符 21
float 关键字 2 - 运算符 22
long 关键字 3 * 运算符 23
short 关键字 4 / 运算符 24
double 关键字 5 = 运算符 25
bool 关键字 6 == 运算符 26
char 关键字 7 < 运算符 27
if 关键字 8 > 运算符 28
else 关键字 9 <= 运算符 29
while 关键字 10 >= 运算符 30
do 关键字 11 ++ 运算符 31
for 关键字 12 – 运算符 32
try 关键字 13 += 运算符 33
void 关键字 14 -= 运算符 34
case 关键字 15 ( 分界符 35
switch 关键字 16 ) 分界符 36
return 关键字 17 { 分界符 37
throw 关键字 18 } 分界符 38
static 关键字 19 [ 分界符 39
main 关键字 20 ] 分界符 40
, 分界符 41
; 分界符 42
整形常量 整形常量 43
浮点型常量 浮点型常量 44
标识符 标识符 45

单词结构文法G[S]
S->关键字|运算符|分界符|整型常量|浮点型常量|标识符
关键字->int|float|long|short|double|bool|char|if|else|while|do|for|try|void|case|switch|return|throw|static
运算符->+|-||/|=|==|<|>|<=|>=|++|–|+=|-=
分界符->(|)|{|}|[|]|,|;|//|/
|/
digit->0|1|2|3|4|5|6|7|8|9
整型常量->digit (digit)

浮点型常量->digit(digit).digit(digit)
letter->a|b|c|d…|z|A|B|…|Z|_
标识符->letter(letter|digit)*

单词状态转化图

错误处理

  • 单词拼写错误
    当前状态与当前输入符号在转换表中的对应项为空

  • 非法字符
    不属于标识符定义规范同时也不属于定义字符的,例如@ ~ $ 中文字符等

    若检测到了非法字符,则程序报错并给出具体报错位置;若检测到单词拼写错误,则报错并给出报错的具体位置,程序终止

代码部分

#include<iostream>
#include<fstream>
#include<string>
#include<vector>
#include<algorithm>
#include<map>
#include<set>
using namespace std;

vector< pair<string, int> > v;
set<string> keys;    //存放关键字的集合
string keysArray[] = { "int","float","long","short","double","bool","char","if","else","while","do","for","try",
"void","case","switch","return","throw","static","main"};
string token = "";  //用于存储单词

enum TokenCode
{
	/*未定义字符例如\t \n等*/
	TK_UNDEF = 0,

	/* 关键字*/
	KW_INT = 1,  //int 关键字	
	KW_FLOAT,    //float关键字
	KW_LONG,     //long关键字
	KW_SHORT,    //short关键字
	KW_DOUBLE,   //double关键字
	KW_BOOL,     //bool关键字
	KW_CHAR,     //char关键字
	KW_IF,       //if关键字
	KW_ELSE,     //else关键字
	KW_WHILE,    //while关键字
	KW_DO,       //do关键字
	KW_FOR,      //for关键字
	KW_TRY,      //try关键字
	KW_VOID,     //void关键字
	KW_CASE,     //case关键字
	KW_SWITCH,   //switch关键字
	KW_RETURN,   //return关键字
	KW_THROW,    //throw关键字
	KW_STATIC,   //static关键字
	KW_MAIN,     //main关键字‘

	/*运算符*/
	TK_PLUS,     //运算符+
	TK_MINUS,    //运算符-
	TK_MUL,      //运算符*
	TK_DIV,      //运算符 /
	TK_ASSIGN,   //赋值语句
	TK_EQ,       //等于号
	TK_LT,       //小于
	TK_GT,       //大于
	TK_LEQ,      //小于等于
	TK_GEQ,      //大于等于
	TK_PPLUS,    //++
	TK_MMINUS,   //--
	TK_PEQ,      //+=
	TK_MEQ,      //-=

	/*分界符*/
	TK_OPENPA,   // (
	TK_CLOSEPA,  // )
	TK_BEGIN,    // {
	TK_END,      // }
	TK_OPENBR,   //[
	TK_CLOSEBR,  //]
	TK_COMMA,    //逗号
	TK_SEMOCLON, //分号;
	TK_INT,      //整型常量
	TK_DOUBLE,   //浮点型常量

	/*标识符*/
	TK_IDENT,    //标识符
};
/* 报错信息
*/
enum WrongInfo {
	WI_UDSG = 1,    //未定义的符号 例如 # @等
	WI_WID,         //错误的标识符声明
};
TokenCode code = TK_UNDEF;   //记录单词的种别码
WrongInfo err = WI_UDSG;     //记录报错的信息
struct Position {
	int column; //列
	int line;   //行
};

bool isLetter(char c) {
	if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) return true;
	return false;
}

bool isDigit(char c) {
	if (c >= '0' && c <= '9') {
		return true;
	}
	return false;
}


int getKeyById(string token) {
	int keysArray_size = sizeof(keysArray)/sizeof(keysArray[0]);
	for (int i = 0; i < keysArray_size; i++) {
		if (token==keysArray[i]) {
			return i + 1;
		}
	}
}

void printToken(TokenCode code,Position pos) {
	string op = " ";  //表示分界符和运算符
	if((code>=1 && code <= 20) || code == 45 || code == 43 || code == 44){
		//switch (code) {
		//case KW_INT:
		//case KW_FLOAT:
		//case KW_LONG:
		//case KW_SHORT:
		//case KW_DOUBLE:
		//case KW_BOOL:
		//case KW_CHAR:
		//case KW_IF:
		//case KW_ELSE:
		//case KW_WHILE:
		//case KW_DO:
		//case KW_FOR:
		//case KW_TRY:
		//case KW_VOID:
		//case KW_CASE:
		//case KW_SWITCH:
		//case KW_RETURN:
		//case KW_THROW:
		//case KW_STATIC:
		//case KW_MAIN:
		//case TK_IDENT:
		//case TK_INT:
		//case TK_DOUBLE:
			cout << '<' << code << ',' << token << '>' << endl;
		//}
	}
	else if (code >= 21 && code <= 42) {
		switch (code) {
		case TK_PLUS: op = "+";
			break;
		case TK_MINUS: op = "-";
			break;
		case TK_MUL: op = "*";
			break;
		case TK_DIV: op = "/";
			break;
		case TK_ASSIGN: op = "=";
			break;
		case TK_EQ: op = "==";
			break;
		case TK_LT: op = "<";
			break;
		case TK_GT: op = ">";
			break;
		case TK_LEQ: op = "<=";
			break;
		case TK_GEQ: op = ">=";
			break;
		case TK_PPLUS: op = "++";
			break;
		case TK_MMINUS: op = "--";
			break;
		case TK_PEQ: op = "+=";
			break;
		case TK_MEQ: op = "-=";
			break;
		case TK_OPENPA: op = "(";
			break;
		case TK_CLOSEPA: op = ")";
			break;
		case TK_BEGIN: op = "{";
			break;
		case TK_END: op = "}";
			break;
		case TK_OPENBR: op = "[";
			break;
		case TK_CLOSEBR: op = "]";
			break;
		case TK_COMMA: op = ",";
			break;
		case TK_SEMOCLON: op = ";";
			break;
		}
		cout << '<' << code << ',' << op << '>' << endl;
	}
	else if (code == 0) {
		switch(err){
		case WI_UDSG: cout << "Wrong! Undefined signal! Error happen at line" << pos.line << endl;
			break;
		case WI_WID: {
			err = WI_UDSG;
			cout << "WRONG Identifier declare! Error happen at line" << pos.line << endl;
			break; 
		}
		}
	}
	
}

void lexicalAnalysis() {
	ifstream ifs;
	ifs.open("test.txt", ios::in);
	char byte = 0;
	Position cur = { 0, 1};  // #表示未定义字符
	while ((byte=ifs.get()) != EOF) {
		if (byte == ' ' || byte == '\t') {
			// 遇到空格跳过
			if (byte == '\t') {
				cur.column += 4;
			}
			else {
				cur.column += 1;
			}
			continue;
		}
		else if (byte == '\n') {
			//遇到换行符
			cur.column = 1;
			cur.line += 1;
			continue;
		}
		else if (isLetter(byte)) {
			//标识符或者是关键字
			token = "";     //token初始化
			do{
				token.push_back(byte);
				cur.column += 1;     //当前的字符位置行不变,列+1
				byte = ifs.get();    //获取下一个字符
			} while (isLetter(byte) || isDigit(byte) || byte == '_');  //循环结束说明当前token中已经存储一个完整的单词
			ifs.seekg(-1, ios::cur);    //往前退一个字母
			set<string>::iterator pos = keys.find(token);  //查找token是否是关键字
			if (pos != keys.end()) {
				//在keys中找到了 说明token是关键字
				code = TokenCode(getKeyById(token));
			}
			else {
				code = TK_IDENT;  //若找不到说明是标识符
			}
		}
		else if (isDigit(byte)) {  //浮点型常数和整型常数以数字开头
			/*
			* 这里没有考虑其他进制的情况以及都没有考虑的报错问题
			*/
			token = "";  //token初始化
			bool other_flag = false;  //表示是其他的进制表示方法
			if (byte == '0') { //判断除十进制以外的其他进制
				token.push_back(byte); //将0压入token串里
				char nextByte = ifs.get();
				if (nextByte == 'b' || nextByte == 'B' || nextByte == 'x' || nextByte == 'X') {
					//若是上述的字母出现在0后面是正确的,压入这些字母继续判断
					other_flag = true;
					token.push_back(nextByte);
					byte = ifs.get();
				}
				else
					ifs.seekg(-1, ios::cur);
			}
			bool isDouble = false;  //判断是否是浮点数
			while (isDigit(byte)) {
				token.push_back(byte);
				byte = ifs.get();
				if (byte == '.' && isDouble == false) { //检查出该字符是. 并且是第一次检查出
					//小数点的下一位也是数字
					if (isDigit(ifs.get())) {
						isDouble = true;
						token.push_back(byte);		//将小数点存入token中
						ifs.seekg(-1, ios::cur);	//将超前读取的小数点后一位数字重新读取
						byte = ifs.get();
					}
				}
			}
			ifs.seekg(-1, ios::cur);
			if (isDouble) {
				code = TK_DOUBLE;   //单词为浮点型
			}
			else {
				code = TK_INT;      //单词为整型
			}

			//接下来判断该单词是否是一个错误的标识符(不能以数字开头)
			byte = ifs.get();
			if (!isDigit(byte) && byte != ' ' && byte != '\t'&& byte!=';'&& !other_flag) {
				code = TK_UNDEF;
				err = WI_WID;
			}
			else {
				ifs.seekg(-1, ios::cur);
			}
		}
		else {
			//最后一种情况是运算符和分界符
			switch (byte) {
			case '+':
			{
				byte = ifs.get();  //读取下一个字符
				if (byte == '+')
					code = TK_PPLUS;  // ++
				else if (byte == '=')
					code = TK_PEQ;    // +=
				else {
					code = TK_PLUS;   //+
					ifs.seekg(-1, ios::cur); //其他情况下需要重新读取下一个字符
				}
			}
			break;
			case '-':
			{
				byte = ifs.get(); //读取下一个字符
				if (byte == '=')
					code = TK_MEQ; // -=
				else if (byte == '-')
					code = TK_MMINUS; //--
				else {
					code = TK_MINUS; //-
					ifs.seekg(01, ios::cur);
				}
			}
			break;
			case '*': code = TK_MUL;
				break;
			case '/':
			{
				if (ifs.get() == '/') {
					while(ifs.get()!='\n'){
						;
					}
					ifs.seekg(-1, ios::cur);
					break;
				}
				else {
					code = TK_DIV;
					ifs.seekg(-1, ios::cur);
					break;
				}
			}
			case '=':
			{
				byte = ifs.get();  //读取下一个字符
				if (byte == '=') {  // ==
					code = TK_EQ;  // 判断是否相等符号
				}
				else {
					code = TK_ASSIGN;  //单词为=
					ifs.seekg(-1, ios::cur);  //回退一个字符
				}

			}
			break;
			case '<':
			{
				byte = ifs.get();   //读取下一个字符
				if (byte == '=') {  // 小于等于
					code = TK_LEQ;
				}
				else {
					code = TK_LT; //小于
					ifs.seekg(-1, ios::cur); //回退一个字符
				}
			}
			break;
			case '>':
			{
				byte = ifs.get();   //读取下一个字符
				if (byte == '=') {
					code = TK_GEQ;    //大于等于
				}
				else {
					code = TK_GT;  //大于
					ifs.seekg(-1, ios::cur);  //回退一个字符
				}
			}
			break;
			// 分界符
			case '(': code = TK_OPENPA;
				break;
			case ')': code = TK_CLOSEPA;
				break;
			case '{': code = TK_BEGIN;
				break;
			case '}': code = TK_END;
				break;
			case '[': code = TK_OPENBR;
				break;
			case ']': code = TK_CLOSEBR;
				break;
			case ',': code = TK_COMMA;
				break;
			case ';': code = TK_SEMOCLON;
				break;
			//未识别符号
			default: code = TK_UNDEF;
			}
		}
		printToken(code,cur);
	}
}
int main() {
	int keysArray_size = sizeof(keysArray) / sizeof(keysArray[0]);
	//初始化关键字集合 
	for (int i = 0; i < keysArray_size; i++) {
		keys.insert(keysArray[i]);
	}
	ifstream ifs;
	ifs.open("test.txt", ios::in);
	if (!ifs.is_open()) {
		cout << "文件打开失败" << endl;
		return 0;
	}
	lexicalAnalysis();
	return 0;
}
编译原理词法分析器和语法分析器的实现C++) 编译原理中的词法分析器和语法分析器是编译器的重要组成部分,它们分别负责将源代码转换为词法单元和抽象语法树。以下是一个简化的项目介绍,描述了如何使用C++实现这两个分析器。 ### 项目介绍: **目标**:使用C++实现一个简单的编译器前端,包括词法分析器和语法分析器。 **主要任务**: 1. **词法分析器**: - 定义词法规则,包括关键字、标识符、常数、运算符和分隔符等。 - 使用有限自动机理论实现词法分析器,能够将源代码转换为词法单元序列。 - 处理词法错误,如非法字符或格式错误的数字。 2. **语法分析器**: - 定义语法规则,构建上下文无关文法(CFG)。 - 使用递归下降解析器或LL(1)解析器实现语法分析器,能够将词法单元序列转换为抽象语法树(AST)。 - 处理语法错误,如语法错误和类型错误。 **技术要求**: - 熟悉C++编程语言。 - 了解编译原理中的词法分析、语法分析概念。 - 熟悉有限自动机理论和抽象语法树。 **开发工具**: - C++编译器,如GCC、Clang。 - 代码编辑器或IDE,如Visual Studio、Code::Blocks或Eclipse。 ### 适合人员: - 计算机科学或相关领域的学生:此项目能够帮助他们实践编译原理和C++编程知识。 - 软件开发者:特别是那些对编译器和解释器如何工作感兴趣的程序员。 - 语言处理领域的研究者:此项目可以作为自然语言处理和编译技术的一个研究起点。 ### 额外建议: - 从一个简单的语言子集开始,逐步增加支持的语法和语义特性。 - 使用单元测试和集成测试来验证分析器的正确性。 - 编写详细的文档,记录设计决策、实现细节和测试结果。 - 考虑使用版本控制系统(如Git)来管理项目代码。 通过实现这样一个编译器前端,学生可以深入理解编译器的工作原理,提高C++编程技能,并为进一步学习编译原理和编程语言理论打下基础。此外,这个项目对于希望进入编译器设计、程序分析和代码生成等领域的人来说,是一个很好的实践机会。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值