SNL语言词法编译器----编译原理课程设计1

 主要思路:从一个文件中,得到一个个的字符,然后对字符进行判断处理,在判断的时候会用到DFA(有限自动机)进行状态之间的转换。

得到的单词主要分为几种类型:标识符、保留字、数字、标点符号、将他们分别进行处理,整理后放在一个token序列中。

token主要由两个部分组成:1)类型  2)内容

程序用到的知识点:

1)C语言文件的读写

举例:

if ((fw = fopen("D:\\jg.txt", "w")) == NULL)
	{
		printf("文件打开失败\n");
		exit(1);
	}
	else {....}

“w”参数部分是指文件的打开方式为只写

“r”只读

“w+”以读写打开方式打开文件,若无此文件,创建文件

“r+”以读写的方式打开文件,若无此文件,出错

*************************************************************************************************************************************************本代码中使用的文件处理函数:

fgetc(f);  //其中f为文件类型指针

ungetc(ch,f);//此函数的作用是将文件类型的指针返回一个位置,char型变量ch,写入这个位置

fprintf(".....",.......);  //将内容写入文件中

2)有限自动机的实现

主要是利用  if  .....else.....语句进行状态之间的转换。

3)全局变量和容器的使用

容器的库include<vector> 

申请一个库:vector<类型>名称

 

 

 

 

 

 

 

实验环境:VS2017

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <vector>
#include <string>
using namespace std;
//定义两个表,分别为标识符表和常量表,为全局

vector<string>bta;
vector<string>cta;
int error = 0;//定义一个全局变量用来保存是否这个文件存在词法问题

//定义TOKEN结构
class Token
{
public:
	Token(string str, int i) //构造函数
	{
		type = str;
		id = i;
	};
	~Token() {};

	string type;
	int id;
};

class Scan 
{
public:
	bool reservedLookup(string str);//判断是是否为保留字
	Token* scanner(FILE *f);
	int addbta(string str);//查表,若表中含有该标识符返回标识符的id,
							//否则将该标识符加入表中
	int addcta(string str);
	bool Isother(char a);//判断是否为字母和数字以外其他的
	bool Isletter(char a);
	bool Isnumber(char a);
};


bool Scan::reservedLookup(string str) 
{
	printf("%s\n", str.c_str());
	string ta[21] =
	{ "begin","integer","char","program","array","of","record","end","var","proceduce"
		,"if","then","else","fi","while","do","endwh","read","write","return","type" };
	for (int i = 0; i < 21; i++) 
	{
		if (str == ta[i]) 
		{
			cout << "为保留字";
			
			printf("%s\n", str.c_str());
			return true;
		}
	}
	return false;
}

int Scan::addbta(string str) 
{
	for (int i = 0; i < bta.size(); i++) 
	{
		if (str == bta[i])
			return i;
	}
	//没找到,加入
	bta.push_back(str);
	return bta.size() - 1;
}

int Scan::addcta(string str) 
{
	for (int i = 0; i < cta.size(); i++) 
	{
		if (str == cta[i])
			return i;
	}
	cta.push_back(str);
	return cta.size() - 1;
}

bool Scan::Isother(char a) 
{
	if (a >= 'A'&&a <= 'Z')
		return false;
	if (a >= 'a'&& a <= 'z')
		return false;
	if (a >= '0'&& a <= '9')
		return false;
	return true;
}

bool Scan::Isletter(char a) 
{
	if (a >= 'A' && a <= 'Z')
		return true;
	if (a >= 'a'&& a <='z')
		return true;
	return false;
}

bool Scan::Isnumber(char a) 
{
	if (a >= '0' && a <= '9')
		return true;
	return false;
}

Token * Scan::scanner(FILE * f) //用来返回一个Token,然后将其加入到序列中,直到不产生
{
	char ch;
	string str = "";//初始为空串
	Token * t = NULL;
	ch = fgetc(f);
	if (ch != EOF) 
	{
		while (ch == ' ' || ch == '\n' || ch == '\t') 
		{
			ch = fgetc(f);
		}//去掉所有的空格

		//根据第一个字母判断各种状态
		if (Isletter(ch)) //如果第一个字母为英文字母
		{
			cout << "进入标识符状态" << endl;
			str = str + ch;
			ch = fgetc(f);
			while (Isletter(ch) || Isnumber(ch)) 
			{
				cout << "再次进入" << endl;
				str = str + ch;
				ch = fgetc(f);
			}
			if (Isother(ch)) //再次读入的不是一个字符或者数字
			{
				cout << "判断为其他的字符串" << endl;
				//将指针返回一个
				ungetc(ch,f);
				//判断是否为关键字
				if (reservedLookup(str)) 
				{
					//如果是保留字,生成一个新的token
					printf("%s\n", str.c_str());
					t = new Token(str, -2);
					return t;
				}
				else
				{
					//如果为标识符
					int id = addbta(str);
					printf("%s\n", str.c_str());
					t = new Token("标识符", id);
					return t;
				}
			}
		}

		if (Isnumber(ch)) 
		{
			cout << "进入数字状态" << endl;
			//如果第一个字符为一个数字
			str = str + ch;
			ch = fgetc(f);
			while (Isnumber(ch))
			{
				str = str + ch;
				ch = fgetc(f);
			}
			if (Isother(ch)) 
			{
				ungetc(ch, f);
				int id = addcta(str);
				t = new Token("数字", id);
				printf("%s\n", str.c_str());
				return t;
			}
		}

		if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(' ||
			ch == ')' || ch == ';' || ch == '[' || ch == ']' || ch == '=')
		{
			cout << "进入运算符号状态" << endl;
			str += ch;
			t = new Token(str, -1);
			printf("%s\n", str.c_str());
			return t;
		}

		if(ch == ':')
		{
			cout << "进入:号状态" << endl;
			str = str + ch;
			ch = fgetc(f);
			if (ch == '=') 
			{
				str = str + ch;
				t = new Token(str, -1);
				printf("%s\n", str.c_str());
				return t;
			}
			else 
			{
				printf("非法符号 %c \n", ch);
				error = 1;
				return NULL;
			}
		}

		if (ch == '{') //注释部分的处理
		{
			cout << "进入注释处理状态" << endl;
			while (ch != '}') 
			{
				ch = fgetc(f);
			}
			t = new Token("zhushi", -4);
			return t;
		}

		if (ch == '.') 
		{
			cout << "进入数组下届判断状态" << endl;
			str = str + ch;
			ch = fgetc(f);
			if (ch == '.') 
			{
				str = str + ch;
				t = new Token("数组下限符", -1);
				printf("%s\n", str.c_str());
				return t;
			}
			else
			{
				return NULL;
			}
		}

		if (ch == ',') 
		{
			cout << "进入逗号判断状态" << endl;
			str = str + ch;
			ch = fgetc(f);
			if (Isnumber(ch) || Isletter(ch)) 
			{
				ungetc(ch, f);
				t = new Token(str, -1);
				printf("%s\n", str.c_str());
				return t;
			}
			else
			{
				str = str + ch;
				printf("非法符号 %s \n", str);
				error = 1;
				return NULL;
			}
		}

		printf("非法符号 %c \n", ch);
		error = 1;
		return NULL;
	}
	else
	{
		return NULL;
	}
}


int main(void) 
{
	FILE *fp;
	if ((fp = fopen("D:\\ce.txt", "r")) == NULL) 
	{
		printf("文件打开失败\n");
		exit(1);
	}


	/*char a;
	a = fgetc(fp);
	printf("%c\n", a);
	cout << "ceshi shifouleyi " << endl;*/

	Scan * scan = new Scan();
	vector<Token>t;//本容器用来存储所有的Token序列
	//申请第一个节点
	Token *token = NULL;
	while ((token = scan->scanner(fp))!= NULL)
	{
		t.push_back(*token);
		token = NULL;
	}
	//关闭文件
	fclose(fp);
	//将得到的list中的内容写到一个新的文件中,这个文件由程序生成。需要调用函数
	int i = 0;

	//判断是是否有错误
	if (error == 1) 
	{
		cout << "有错误" << endl;
	}
	else
	{
		//第一步打印标识符表

		printf("打印标识符表\n");
		for (i; i < bta.size(); i++)
		{
			printf("%d---%s\n", i, bta[i].c_str());
		}

		printf("打印数字表\n");
		for (i = 0; i < cta.size(); i++)
		{
			printf("%d---%s\n", i, cta[i].c_str());
		}

		printf("打印所有的Token序列\n");
		for (i = 0; i < t.size(); i++)
		{
			printf("%d---%s---%d\n", i, t[i].type.c_str(), t[i].id);
		}

	}


	//将所得到的及结果放到一个文件中进行存储
	//这里需要进行文件的写操作

	FILE *fw;
	if ((fw = fopen("D:\\jg.txt", "w")) == NULL)
	{
		printf("文件打开失败\n");
		exit(1);
	}
	else 
	{
		for (i = 0; i < t.size(); i++)
		{
			if (t[i].type != "zhushi") 
			{
				fprintf(fw, "[%s]---[%d]\n", t[i].type.c_str(), t[i].id);
			}
			
		}
		
	}
	fclose(fw);
	system("pause");
	return 0;
}


在编写程序过程中遇到的问题:

1.C语言中string类型的输出:

在采用printf或者cout<<进行输出的时候都会出现乱码的现象,但是利用字符串调用.c_str()函数该现象就会消失

原因:printf函数是针对char*的,故要正确显示,通过c_str()返回首地址。

2)VS2017在使用文件打开或者关闭的时候会出现unsafe错误

处理办法:右键工程名----->属性---->C/C++----->预处理器------>预处理器定义---->在右边的输入框中加入_CRT_SECURE_NO_WARNINGS 然后点击保存。

3)C++中可以使用流的方式读取文件的内容,以getline函数读内容,举例如下:

#include <fstream>




char buffer[256];
	fstream ex("D:\\ce.txt");//注意这个地方一定要是两个\\,否则是识别不出来的
	if (!ex.is_open()) 
	{
		cout << "文件打开失败" << endl;
		exit(0);
	}
	while (!ex.eof()) 
	{
		ex.getline(buffer, 13);
		cout << buffer << endl;
	}

4.字符串可以利用+号直接在后面追加 ,或者可以利用append函数(该函数也是将字符加入当当前字串的尾部)

本文章参考资料:https://blog.csdn.net/kyt511/article/details/46490845

https://www.cnblogs.com/zyrblog/p/6885922.html

 

 

  • 5
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值