【编译原理 课程设计】设计SAMPLE语言的语法分析器 github源码(C++、LL(1)分析、语法制导翻译和中间代码生成)

这里是lz大二上的编译原理实验课,课设要求模仿编译器的原理思想,用编译语言设计能够识别单词和简单语法的编译,当时摸了两天左右写了小一千行,在这里再回顾一下思路,并留下对应的源码供参考。

实验实现的方法有很多种,其中实验一的词法分析部分比较简单,这里重点分析语法分析部分。

题目需求:

 

实验一:词法分析

        这里用了stl::map等提前存储单词记录出现次数即可。这里就不展开详细描述了。一些具体细节的可参考github中的源码。

map<string, int> sampleDictionary;
void sampleDictionaryInit() //写好对应映射
{
	sampleDictionary["and"] = 1;
	sampleDictionary["array"] = 2;
	...
	sampleDictionary["["] = 59;
	sampleDictionary["]"] = 60;
	return;
}
//写出一些会用到的工具类函数,和二元式的数据结构
struct DoubleGroup {
	int a;
	int b;
	string Word;
	DoubleGroup(string w,int x = 0, int y = 0)
	{
		Word = w;
		a = x;
		b = y;
	}
};
	bool isKeyWord(string input) //是 关键字
	{
		if (sampleDictionary[input])
		{
			if (sampleDictionary[input] >= 1 && sampleDictionary[input] <= 35)
				return true;
		}
		return false;
	}
	bool isNum(char Input) //是 数字
	{
		if (Input >= '0' && Input <= '9')
			return true;
		else
			return false;
	}
	
	bool isLetter(char Input)//是 字母
	{
		return (Input >= 'a' && Input <= 'z') || (Input >= 'A' && Input <= 'Z');
	}
	bool isDivided(string Input) //是 分隔符
	{
		if (sampleDictionary[Input])
		{
			if (sampleDictionary[Input] >= 39 && sampleDictionary[Input] <= 60)
				return true;
		}
		if (Input == " " || Input == "\n")
			return true;
		return false;
	}

实验二:语法分析

        这里具体展开讲讲实验二中的实现方法

 程序核心思想:LL(1)分析法让产生式右部逐个文法符号与输入串匹配,每当一个文法符号获得匹配,就可以执行语义动作。

语法分析用到的函数原型

    //语法分析用到的函数原型:
	void Gramma_analysis();   //语法分析,负责逐字扫描单词,压语义栈,跳转到对应语义动作
	void Gramma_Expression();  //表达式的四元式翻译
	void Gramma_Expression(int);
	void Gramma_Assignment();  //赋值句的四元式翻译
	EState& Gramma_Condition(); //bool式的四元式翻译,返回整个bool式子的状态E
	void Gramma_FrontQuaternions(); //四元式生成前调用,在这里匹配语义栈执行对应语义动作
	void Gramma_Quaternions_MergeJump(); //在最终输出前先合并一次jump

语法分析用到的数据结构(struct):

其中,一些表达式的属性需要记录,比如bool表达式需要知道入口,一些条件需要对应的真出口与假出口等,并设置根据链表的回填方法。

//四元组
struct Quaternion {
	string v1, v2, v3, v4;
	int index;
	Quaternion(int _index, string _v1 = "-", string _v2 = "-", string _v3 = "-", string _v4 = "-")
	{
		index = _index;
		v1 = _v1;
		v2 = _v2;
		v3 = _v3;
		v4 = _v4;
	}
};
vector<Quaternion> Quaternions;
//bool表达式的属性
struct EState
{
	int selfpos; //自己在四元组中对应的位置
	int _true; //真出口
	int _false; //假出口
	bool isCorrect; //锁定 真出口&假出口,给e1 or e2 或 e1 and e2情况下使用
	EState* _trueList; //回填真出口的链表
	EState* _falseList;//回填假出口的链表
	EState(int _index) //构造函数
	{
		selfpos = _index;
		_true = -1;
		_false = -1;
		_trueList = NULL;
		_falseList = NULL;
		isCorrect = false;
	}
	EState*& GetTlist() //得到需要回填的真出口链表尾
	{
		if (_trueList == NULL)
			return _trueList;
		else
			return _trueList->GetTlist();
	}
	EState*& GetFlist() //得到需要回填的假出口链表尾
	{...}
	void backpatch_T(int exit) //回填真出口
	{
		_true = exit;
		if (selfpos > 0 && !isCorrect)
		{
			Quaternions[selfpos].v4 = ToString(_true);
		}
		if (_trueList == NULL)
			return;
		else
			_trueList->backpatch_T(exit);
	}
	void backpatch_F(int exit)//回填假出口
	{...}
};
//if while repeat的属性
struct if_State
{
	EState* E;      //对应的bool表达式
	int pos_ElseQ;  //如果该if出现了对应的else,要记录该E.false的入口和E.true的出口
	if_State(EState& _temp)
	{
		E = &_temp;
		pos_ElseQ = -1;
	}
	void backpatch_T(int exit)
	{
		E->backpatch_T(exit);
	}
	void backpatch_F(int exit)
	{
		E->backpatch_F(exit);
	}

};
struct while_State
{
	int Enter; //while入口编号
	EState* E; //对应的bool表达式
	void backpatch_T(int exit)
	{
		E->backpatch_T(exit);
	}
	void backpatch_F(int exit)
	{
		E->backpatch_F(exit);
	}
	while_State(EState& _temp,int _enter = _curr_index)
	{
		E = &_temp;
		Enter = _enter;
	}
};
struct repeat_State
{
	int Enter; //repeat入口编号
	EState* E; //对应的bool表达式
	repeat_State(int _enter)
	{
		Enter = _enter;
		E = NULL;
	}
	void backpatch_T(int exit)
	{
		E->backpatch_T(exit);
	}
	void backpatch_F(int exit)
	{
		E->backpatch_F(exit);
	}
};

存储struct的存储结构(STL)如下

stack<string> StateStack; //语义栈 只会出现if\else\then\while\do的动作符

stack<if_State> ifStack;

stack<while_State> whileStack;

stack<repeat_State> repeatState;

list<EState> e_Storage;   //bool表达式、储存ei条件的状态

list<EState> E_stateList; //储存E条件的状态

语法分析Gramma_analysis()函数:(通过Quaternions.push_back()生成四元组)

实验一中已将每个单词存入words数组(string)中,通过扫描words数组的单词,当该单词有对应的语义动作时,调用对应的语义动作:

  1. 当前单词为:=时,调用赋值句的生成函数Gramma_Assignment();
  2. 当前单词为if\while\until时:先调用Gramma_Condition()生成对应的bool表达式和四元式,然后生成自身的属性,压入语义栈
  3. 当前单词为else\repeat\then\do时:压入语义栈

 这里写一下对应的伪代码

void sampleAnalyze::Gramma_analysis()
{
	while (循环单词表)
	{
		{...}
		if (如果当前单词为:=)
		{
			Gramma_Assignment();
		}
		if (如果当前单词为"if")
		{
			Gramma_FrontQuaternions();
			ifStack.push(Gramma_Condition());
			StateStack.push("if");
		}
		if (如果当前单词为"while")
		{
			Gramma_FrontQuaternions();
			int _enter = _curr_index;
			whileStack.push( while_State(Gramma_Condition(),_enter) );
			StateStack.push("while");
		}
		if (如果当前单词为"repeat")
		{
			repeatState.push( repeat_State(-1) ); //标记需要回填repeat的入口
		}
		if (如果当前单词为"then"/"else"/"do")
		{
             //压入语义栈
			StateStack.push("then")/StateStack.push("else")/StateStack.push("do");
		}
		if (如果当前单词为"until")
		{
			repeatState.top().E = &Gramma_Condition();
			repeatState.top().backpatch_F(repeatState.top().Enter);//假出口为repeat的入口
			repeatState.top().backpatch_T(_curr_index);//真出口为until下一行
	repeatState.pop();
		};
{...}
	}
}

赋值式的处理Gramma_Assignment() 标记:=位置,调用算式表达式处理Gramma_Expression()生成对应四元式。

算式表达式处理Gramma_Expression()扫描整个算式符,标记加减+-;乘除*/的位置,先计算乘除*/,再计算加减+-,生成四元式

void sampleAnalyze::Gramma_Expression()
{
	list<int> multip_division; //乘除标志位
	list<int> add_sub; //加减标志位
	while (遍历算式表达式)
	{
		if (如果当前单词为"*" || 如果当前单词为"/")
		{
			记录该单词位置
		}
	}
	Gramma_FrontQuaternions();
	//处理乘除,再处理加减
	while (遍历标记的所有乘除位置)
	{
		根据标记位置的前后单词,产生四元式,产生中间代码Ti
	}
     按照同样逻辑再处理加减运算符
	{...}
}

Bool表达式的分析Gramma_Condition() 先生成一个E表示该个条件式的属性,扫描bool表达式

,每当扫描到>= = <= > < 时生成两个四元组和一个ei,并调用Gramma_Expression()

,e和E的属性使用EState(struct)记录,ei与ej,E之间的关系如下:

  1. 当E -> e1 时,e1和E同true和false出口
  2. 当E -> e1 or e2 时,e1.false = e2.pos , e1.true = e2.true = E.true, e2.false = E.false
  3. 当E -> e1 and e2 时,e1.true = e2.pos , e1.false = e2.false = E.false, e2.true = E.true

不同属性的EState真假出口的通过链表拉链的方式等待回填,函数结束时返回E的地址,留给if\while\repeat属性中的EState*记录

bool IsCondition(string op) //是 bool 符
	{
		return (op == "<") || (op == "<=") || (op == "=") || (op == ">") || (op == ">=");
	}
EState& sampleAnalyze::Gramma_Condition()
{
	生成E
	while (遍历bool表达式)
	{
		if (如果读取到了标识符/字符串/数字)
		{
			Gramma_Expression(_temp_rp);
		}
		if (op == "<") || (op == "<=") || (op == "=") || (op == ">") || (op == ">=") 
		{
			Gramma_Expression(_temp_rp + 1);
			if(如果当前没有标记过的or/and)
			{ 
				生成ei
				生成四元式*2 
                  链表拉链ei与E的真假出口挂钩
			}
			else 
			{
				if (存在标记过的or)
				{
					生成ei
				    生成四元式*2 
                      链表拉链ei与ei-1和E的真假出口挂钩
                      弹出该or的位置
				}
				if (存在标记过的and)
				{...}
			}
		}
		if (如果读取到了and)
		{
			标记and位置
			if(orPoint + andPoint > 1)
			{
				抛出报错:多余的and/or
			}
		}
		{...}
	return E的地址
}

检查语义栈,回填E的出入口Gramma_FrontQuaternions()每次生成四元组前会调用一次,检查语义栈顶的动作词并执行相应动作:

While(语义栈不为空){

  1. 栈顶为"if":(1)如果if.false未回填,回填if.false,否则(if.false已经被else回填情况下)回填已经标记好的if.true出口,continue

2.栈顶为"else":回填if.false,break

3.栈顶为"do":回填while.true,break

4.栈顶为"then":回填if.true,break

5.栈顶为"while":生成四元组(j,-,-,while.enter),然后再回填while.false,continue

6.栈顶为"repeat":标记repeat入口,continue

}

如出现一些异常情况:如if.false已经回填了栈顶依然出现了‘else’,程序会抛出报错

void sampleAnalyze::Gramma_FrontQuaternions()
{
	while (!StateStack.empty())
	{
		if (StateStack.top() == "if")
		{
			if (ifStack.top().E->_true != -1 && ifStack.top().E->_false == -1) 
//true出口已回填,false出口未回填(无else情况)
			{
				ifStack.top().backpatch_F(_curr_index); //回填false出口
			}
			else if (ifStack.top().E->_true == -1) //true出口未回填
			{
				cout << "语法分析错误:if缺少then";
				abort();
			}
			else if (ifStack.top().E->_true != -1 && ifStack.top().E->_false != -1) 
//true出口\false出口已回填(有else情况)
			{
				Quaternions[ifStack.top().pos_ElseQ].v4 = ToString(_curr_index); 
//回填if-exit的出口
			}
			ifStack.pop();
			StateStack.pop();
			continue;
		}
		if (StateStack.top() == "else")
		{
			if (ifStack.top().E->_true != -1 && ifStack.top().E->_false == -1) //true出口已回填,false出口未回填
			{
				ifStack.top().pos_ElseQ = _curr_index;	//标记if-exit的出口
				Quaternions.push_back(Quaternion(_curr_index++,"j"));
				ifStack.top().backpatch_F(_curr_index); //回填假出口
			}
			else if (ifStack.top().E->_true != -1 && ifStack.top().E->_false != -1) //true出口已回填,false出口未回填
			{
				cout << "语法分析错误:出现了多余的else";
				abort();
			}
			else if (ifStack.top().E->_true == -1) //true出口未回填
			{
				cout << "语法分析错误:if缺少then";
				abort();
			}
			StateStack.pop(); //弹出then
			break;
		}
		if (StateStack.top() == "then")
		{
			if (ifStack.top().E->_true != -1) //true出口已回填
			{
				cout << "语法分析错误:多余的then出现";
				abort();
			}
			if (ifStack.top().E->_true == -1) //true出口未回填
			{
				ifStack.top().backpatch_T(_curr_index); //回填if.true
			}
			StateStack.pop();
			break;
		}

		if (StateStack.top() == "while")
		{
			if (whileStack.top().E->_true == -1) //while.true出口未被回填
			{
				cout << "语法分析错误:while缺少do";
				abort();
			}
			//产生新四元式(循环回调)
			Quaternions.push_back(Quaternion(_curr_index++, "j", "-", "-", ToString(whileStack.top().Enter)));
			whileStack.top().backpatch_F(_curr_index);//回填while.false出口

			whileStack.pop();
			StateStack.pop();
			continue;
		}
		if (StateStack.top() == "do")
		{
			if (whileStack.top().E->_true != -1) //while.true出口已被回填
			{
				cout << "语法分析错误:多余的do出现";
				abort();
			}
			//回填while.true出口
			whileStack.top().backpatch_T(_curr_index);

			StateStack.pop();
			break;
		}
	}
//有需要回填的repeat出口(写在这里的目的是为了先把else的四元式生成完)
	if (!repeatState.empty() && repeatState.top().Enter == -1) 
	{
		repeatState.top().Enter = _curr_index;
	}
}

在所有语法分析完成后,合并jump,调PrintQuatrenions()输出所有四元式,程序结束

void PrintQuatrenions()
{
	for (int i = 0; i < Quaternions.size(); i++)
	{
		printf("(%d) (%s,%s,%s,%s)\n", Quaternions[i].index,
			Quaternions[i].v1.c_str(),
			Quaternions[i].v2.c_str(),
			Quaternions[i].v3.c_str(),
			Quaternions[i].v4.c_str());
	}
}

Github源码

对应的项目源码已贴,欢迎大家参考

GitHub - sugarzo/SampleCompilation: 大二编译原理课设-Sample语言的词法/语法分析器(c++)

猜你喜欢

转载自blog.csdn.net/m0_51776409/article/details/124875054