【Suatin】不学编译原理就制作语言11——if-elif-else--------制作信号槽机制

前言

前面做起来挺快的!接下来这个可能要花一周的时间了!需要构造一颗语义树,用来控制解释语句的顺序!


分割语句

主要就只有两种语句,一是普通的语句,二是在 if() 中的语句。下面规定

  • 设置一个小括号计数器,初始化零,遇到左括号加一,遇到右括号减一。当计数器为一时,说明第二种语句开始了,当计数器为零时,第二种语句结束了。
  • 设置一个第二种语句的标志,当此时是在分割第二种语句时,设置此标志为真。分割完第二种语句后,设置为假,即可以开始第一种语句的匹配。第一种语句以分号结尾。
//main.suatin
sum = 0;
if(1==TRUE and (1+2) > 45)
	sum = 1 + 1;
elif(1+3+4)
	sum = 2+2;
	sum = sum +1;
	sum = sum + 1;
end
//Resolver.cpp
	void Resolver::create_ast() {
    
    
		
		bool judge_statement_flag = false;
	

		auto print = [&start,&end]() {
    
    
			for (int k = start; k <= end; ++k) {
    
    
				std::cout << global_infix[k]->name;
			}
			std::cout << "\n";
		};

		for (int it = 0; it < global_infix.size(); ++it) {
    
    
			//处理Token和keyword
			(this->*funcMap[global_infix[it]->type])(it);
			

			if (judge_statement_flag) {
    
     //()内的语句
				//遇到if后面的右括号
				if (count_little == 0) {
    
    
					end = it - 1;//在右括号之前结束
					std::cout << "括号内语句>";
					print();
					judge_statement_flag = false;
					//之后的普通语句在右括号之后开始
					start =  it + 1;
					end = it;
				}
			}
			else {
    
     //普通语句
				if (global_infix[it]->type == SuatinTokenType_Sem) {
    
    
					std::cout << "普通语句>";
					print();
					//之后的普通语句在分号之后开始
					start = end + 1;
				}
				//遇到if后面的左括号
				if (count_little == 1) {
    
    
					start = end = it + 1;//在左括号之后开始
					judge_statement_flag = true;
				}
			}

			++end;
			//每次循环开始和最后,end=it
		}
		
	
		g_error_lex_flag = true;//测试用
		if (g_error_lex_flag)return;//词法期出错就没必要再构造语法树了!!!

		//根据Parser构造语法树
		g_run_time = SuatinRunTimeType_Parse;
		g_statement_index = 0;
		for (std::vector<Parser*>::iterator it = v_exprs.begin(); it != v_exprs.end(); ++it) {
    
    
			(*it)->CreateASTree();
			++g_statement_index;
		}
	}
	void Deal_k_else(int& _t){
    
    
		++start;//else不是语句的内容
		...
	}
	void Deal_k_end(int& _t){
    
    
		++start;//end不是语句的内容
		...
	}
	
普通语句>sum=0;
括号内语句>1==TRUEand(1+2)>45
普通语句>sum=1+1;
括号内语句>1+3+4
普通语句>sum=2+2;
普通语句>sum=sum+1;
普通语句>sum=sum+1;

解释器的类

在这里插入图片描述

大更改——用enum替换typeid

before
	virtual NumExpr* GetClassType(){
    
     return this;}
	
	if(typeid(*(node->GetClassType())) == typeid(NumExpr)){
    
    
		xxx
	}
	
now
	virtual SuatinExprClassType GetClassType(){
    
     return SuatinExprClassType_NumExpr;}
	
	if(node->GetClassType == SuatinExprClassType_NumExpr){
    
    
		xxx
	}

消灭布尔能够被计算的BUG

  1. 在+,-,*,/,^,中检查左边的ID,即exprRoot是否是布尔
  2. 在分号、and、or中检查右边的ID,即exprRoot是否是布尔
			//1.检查左边的ID是否是布尔,布尔是不能用来计算的
			if ((*_node)->GetClassType() == SuatinExprClassType_IDExpr) {
    
    
				IDExpr* node_tmp = dynamic_cast<IDExpr*>(*_node);
				if (node_tmp->GetType() == SuatinIDType_bool) {
    
    
					ThrowException<SuatinErrorType_Syntax>(start, end, "[pow] boolean identifier cannot used for calculate");
					return;
				}
			}

			...
	
			//2.检查右边的ID是否是布尔,布尔是不能用来计算的
			if((*_exprRoot)->GetClassType() == SuatinExprClassType_IDExpr ){
    
     
				IDExpr* node_tmp = dynamic_cast<IDExpr*>((*_exprRoot));
				if (node_tmp->GetType() == SuatinIDType_bool) {
    
    
					ThrowException<SuatinErrorType_Syntax>(start, end, "[sem] boolean identifier cannot used for compare");
					return;
				}
			}

大更改——Run-Time Type Identification

之前构造语法树的时候,在构造的时候确定了==,~=,and,or,not的解释接口,这样在解释的时候就能快捷又准确。但是碰到多行语句的时候出问题了,比如一个变量sum,开始时没解释,在每个有sum变量的语法树中,sum的类型都是nil

sum = 1
if(sum == 2)
	xxx
end

当解释if中的条件时,因为sum是nil 类型,所以 == 左边的解释接口就被设置成了bool类型的了,那么就是默认2是否是真或假了,这里2肯定是真,并且sum是1,也是真,必然执行if的语句块!!!!

所以,确定解释接口,不能在构造的时候确定,要在解释前,构造后确定!!!

//Parser.cpp
	
	/*解释的第一步,确定Abstract Syntax Tree上==,~=,and,or,not的解释接口类型*/
	void	Parser::Confirm_ASTree_InterfaceType() {
    
    
		if (root == NULL || exprNoVal == NULL) {
    
    
			ThrowException<SuatinErrorType_Syntax>("root node was null");
			return;
		}

		_fact_Confirm_ASTree_InterfaceType(exprNoVal);//开始迭代

	}

	//实际的确定接口类型的函数
	void Parser::_fact_Confirm_ASTree_InterfaceType(Expr* _node) {
    
    
		if (_node == NULL)return;

		switch (_node->GetClassType()) {
    
    
		case SuatinExprClassType_NotExpr:
		{
    
    
			NotExpr* node_tmp = dynamic_cast<NotExpr*>(_node);
			//确定not的解释接口类型
			node_tmp->Set_InterfaceType(GetTree_InterfaceType(node_tmp));
			_fact_Confirm_ASTree_InterfaceType(node_tmp->GetContent());
		}
		break;
		case SuatinExprClassType_AndExpr:
		{
    
    
			//DLR遍历
			AndExpr* node_tmp = dynamic_cast<AndExpr*>(_node);
			//确定左边的解释接口类型
			node_tmp->SetLeft_InterfaceType(GetTree_InterfaceType(node_tmp->GetLeft()));
			_fact_Confirm_ASTree_InterfaceType(node_tmp->GetLeft());
			//确定右边的解释接口类型
			node_tmp->SetRight_InterfaceType(GetTree_InterfaceType(node_tmp->GetRight()));
			_fact_Confirm_ASTree_InterfaceType(node_tmp->GetRight());
		}
		break;
		case SuatinExprClassType_OrExpr:
		{
    
    
			OrExpr* node_tmp = dynamic_cast<OrExpr*>(_node);
			//确定左边的解释接口类型
			node_tmp->SetLeft_InterfaceType(GetTree_InterfaceType(node_tmp->GetLeft()));
			_fact_Confirm_ASTree_InterfaceType(node_tmp->GetLeft());
			//确定右边的解释接口类型
			node_tmp->SetRight_InterfaceType(GetTree_InterfaceType(node_tmp->GetRight()));
			_fact_Confirm_ASTree_InterfaceType(node_tmp->GetRight());
		}
		break;
		case SuatinExprClassType_EqEqExpr:
		{
    
    
			EqEqExpr* node_tmp = dynamic_cast<EqEqExpr*>(_node);
			//确定左边的解释接口类型
			node_tmp->SetLeft_InterfaceType(GetTree_InterfaceType(node_tmp->GetLeft()));
			_fact_Confirm_ASTree_InterfaceType(node_tmp->GetLeft());
			//确定右边的解释接口类型
			node_tmp->SetRight_InterfaceType(GetTree_InterfaceType(node_tmp->GetRight()));
			_fact_Confirm_ASTree_InterfaceType(node_tmp->GetRight());
		}
		break;
		case SuatinExprClassType_NeqExpr:
		{
    
    
			NeqExpr* node_tmp = dynamic_cast<NeqExpr*>(_node);
			//确定左边的解释接口类型
			node_tmp->SetLeft_InterfaceType(GetTree_InterfaceType(node_tmp->GetLeft()));
			_fact_Confirm_ASTree_InterfaceType(node_tmp->GetLeft());
			//确定右边的解释接口类型
			node_tmp->SetRight_InterfaceType(GetTree_InterfaceType(node_tmp->GetRight()));
			_fact_Confirm_ASTree_InterfaceType(node_tmp->GetRight());
		}
		break;
		case SuatinExprClassType_EqExpr:
			ThrowException<SuatinErrorType_Type>("Expr pointer was evaluate type");
			return;
		default:
			break;
		}

	}

if-elif-else实现原理

  • 一级语句与一级的 if 块都是放在semantic_tree里的容器中! 没有结束的 if 块都放在uncompleted_leaf中!
  • if 中有条件语句、有块语句、有last链条!解释if时,如果先解释其条件语句并得到该语句的返回真假,如果为真就执行if块中的语句——如果其中有嵌套的if的话,同样的原理——如果为假,就顺着last 链条找下一个elif/else,遇到了elif 的话,原理和遇到 if一样,如果前面都是假,遇到else就直接执行else语句块
  • 单个if语句块用elif/else/end结束,elif语句块用elif/else/end结束,else语句块用end结束。整个if语义树用end结束!
最近匹配原则

遇到elif时,说明同一链条上的if块结束了(一条if-elif-else链条上if只有一个)。
遇到下一个elif/else时,说明同一链条上最近的一个if/elif结束了。
遇到end时,说明同一链条上最近的一个if/elif/else结束了!

通信机制

每条语句构造完后都放在Parser实例内,一条语句一个Parser实例,所有的Parser实例都放在Resolver的容器中!

语义树的解释接口也是无形参,无返回值的,这样的接口比较简洁!在Resolver中构造语义树,语义树中的每个节点都不拥有语句,只拥有每条语句在Parser容器中的索引!!!

因为语义树和语句的分离,所以不通过参数来解释!而通过做一个简单的信号槽机制,意思是做一个信号类、一个函数容器类,然后通过把要操作的函数通过信号类实例放入函数容器中,然后就可以通过信号类实例调用到该函数了!

——说是通信机制,其实本质还是利用全局变量,所以用一个全局的函数指针指向要操作的函数,然后再别的地方使用,这和信号槽机制一个意思!
——但是这样看起来简洁点!代码是给人看的,如果要追求性能,就不给人看了!

//Utils.h
	
	...
	
//Suatin的slot/signals机制
#define emit /*只是表示这个已经被定义了,没有别的意思*/
#define slots
#define signals public
//非成员信号 -> 非成员函数
//非成员信号 -> 成员函数
#define Connect(signal,slot) ((signal).Bind(slot))

	...

	//slot
	template<typename ... Args>
	class SuatinSlot {
    
    
	public:
		using FuncPtr = std::function<void(Args ...)>; //重命名函数容器
	private:
		FuncPtr funcPtr = NULL;
	public:
		SuatinSlot(const FuncPtr& _funcPtr) : funcPtr(_funcPtr){
    
    } //传入待执行的方法

		void Exec(Args ... args) {
    
     
			funcPtr(std::forward<Args>(args)...);  //执行绑定的方法
		}
	};


	//signal
	template<typename ... Args>
	class SuatinSignal {
    
     
	public:
		using SlotPtr = std::shared_ptr<SuatinSlot<Args...>>;//重命名Slot的智能指针
		using FuncPtr = std::function<void(Args ...)>; //重命名函数容器
	private:
		std::vector<SlotPtr> v_slots;
	public:
		void Bind(const FuncPtr& _funcPtr) {
    
     //绑定接受者和方法
			SuatinSlot<Args...>* p = new SuatinSlot<Args...>(_funcPtr);  //生成一个slot实例
			v_slots.push_back(SlotPtr(p));   //放入智能指针,并把智能指针放入slot容器中
		}

		//发射信号
		void operator()(Args...args) {
    
    
		//void Emit(Args ... args){
    
    
			for (auto& it : v_slots) {
    
    
				it->Exec(std::forward<Args>(args)...);//执行所有绑定的方法,因为一个信号能触发多个动作
			}
		}
	};


	//语义树发给Resolver的信号
	extern SuatinSignal<int>* g_signal;
//Resolver.cpp
	Resolver::Resolver(){
    
    
		...
		//初始化信号与槽
		g_signal = new SuatinSignal<int>();
		Connect(*g_signal,  std::bind(&Resolver::_slot_interpret, this, std::placeholders::_1));
		...
	}
	
	...
	
	//接收从语义树中发射的信号
	void Resolver::_slot_interpret(int _index) {
    
    
		if (CheckStatementIndex(_index) == false)return;
		v_exprs[_index]->interpret();
	}


	bool Resolver::CheckStatementIndex(int _index) {
    
    
		//检查索引的范围
		if (_index < 0 || _index >= v_exprs.size()) {
    
    
			std::string s = "index = " + std::to_string(_index) + " was wrong";
			ThrowException<SuatinErrorType_OutRange>(s);
			return false;
		}
		return true;
	}
//Cmd.cpp
	void SingleCmd::interpret() {
    
     
		emit (*g_signal)(index); //发射信号到Resolver中的解释方法
	}

构造if-elif-else嵌套语义树(N叉树)

全局的Block下有N条语句,每条语句都可能有一个if节点,每一个if节点都可能有一条if-elif-…-else链条,每个链条上的节点都有一个Block,每个Block都有M条语句。。。

所以,这么多分叉,遍历起来太麻烦了!!!需要用栈来储存Block,每次遇到if之类的节点,或者是遇到单条块语句,都放在当前的Block里,当前的Block就是未结束的Block,一旦结束就从栈中抛出,取下一个Block作为当前的Block!!!这样就不管是什么叉树,不管有多少层,多少分叉了!!!

  • 准备一个临时变量uncompleted_tree,普通的语句都会被放在semantic_tree中的容器中,完整的if[-elif-else]-end块,也会被放在里面,在还没用变得完整的时候,就把其根节点先放在uncompleted_tree下面!
  • 准备一个栈,放入没有结束的语句块。比如if,在遇到下一个elif或else或end前,都是没有结束的,这时候如果遇到普通语句,就把该语句装入栈顶节点的block中就行了!!!如果遇到的是括号内语句,就设置为栈顶节点的condition了!!!
  • 遇到结束符的栈顶节点,end_flag标志设置为真,并出栈。当栈里没有节点后,就将uncompleted_tree装入semantic_tree中的容器里,置空uncompleted_tree

项目演示

//main.suatin
sum = not TRUE;
if(sum==TRUE)
	sum = -1;
elif(sum)
	sum = -2;
else 
	sum = -3;
	if(sum==-3)
		sum = -4;	
		if(sum==-4)
			sum = -5;
		end
	end
end
初始化语言环境 time consumed 0 ms
词法分析 time consumed 813 ms
普通语句>sum=notTRUE;
括号内语句>sum==TRUE
普通语句>sum=-1;
括号内语句>sum
普通语句>sum=-2;
普通语句>sum=-3;
括号内语句>sum==-3
普通语句>sum=-4;
括号内语句>sum==-4
普通语句>sum=-5;
语法分析·创建语法树 time consumed 131 ms
[result]false
[result]false
[result]false
[result]-3
[result]true
[result]-4
[result]true
[result]-5
语法分析·解释语法树 time consumed 28 ms
suatin environment>
                name   isconst    type      funcPtr     flag             num   str
                 NIL    true       nil     00000000    false               0
               FALSE    true      bool     00000000    false               0
                TRUE    true      bool     00000000     true               0
                 sum   false    number     00000000     true              -5
显示环境信息 time consumed 225 ms
释放环境 time consumed 0 ms
program time consumed 1229 ms
请按任意键继续. . .

项目代码地址CSDN
https://download.csdn.net/download/weixin_41374099/12255241
项目代码地址BDWP
链接:https://pan.baidu.com/s/1KXHyGx8Xelkx23TCoE5FkA
提取码:zut6

参考:
C++11实现Qt的信号机制

猜你喜欢

转载自blog.csdn.net/weixin_41374099/article/details/104775188
今日推荐