自定义脚本引擎开发纪实 -逆波兰求值

自定义脚本引擎开发纪实

  • 第一章
    • 如何求解一个表达式的值
    • 代码例子

第一章

脚本语言的特点,咱们不再赘述了。自定义的脚本引擎还有许多亟待优化的地方,不过不碍我们讲解如何实现自定义脚本引擎。
本人呢,代码水平很菜,肯定会出现很多的错误,大家发现了呢,可以随手在自己实现的过程中改了,顺带给我提个醒儿,这儿先谢谢您呐!同时呢,这个是优化前的代码,自己可以用更高效的方法改写,提高效率。
下面会把自己当时写的验证demo,贴出来,那是我的起步。代码质量有点差,大家凑合看吧。

如何求解一个表达式的值

此处呢,先从简单的说起。咱们先抛开字符串和浮点数。先看看整数吧。
老师说过,计算一个表达式的值,可以用逆波兰表达式(想不起来的同学,可以搜索下,瞅一眼)。咱们就用这个试试,先把表达式转成逆波兰表达式的形式,然后按照逆波兰表达式的步骤进行计算。
第2步呢,想想你要支持的运算符,设置一下他们的优先级。然后就可以求解了。

代码例子

//calcstrvalue.cpp
#include "stdafx.h"
#include "calcstrvalue.h"
#include "automicopt.h"
#include 

int GetOperatorPriorityA(LPCSTR lpszOp, int& nOpCount)
{
	if(strcmp(lpszOp, "+") == 0 || strcmp(lpszOp, "-") == 0)
	{
		nOpCount = 2;	//操作数个数
		return 10; //优先级
	}
	else if(strcmp(lpszOp, "*") == 0 || strcmp(lpszOp, "/") == 0)
	{
		nOpCount = 2;
		return 11;
	}
	else if(strcmp(lpszOp, ">") == 0 || strcmp(lpszOp, "<") == 0 || strcmp(lpszOp, "==") == 0 || strcmp(lpszOp, "!=") == 0
		|| strcmp(lpszOp, ">=") == 0 || strcmp(lpszOp, "<=") == 0)
	{
		nOpCount = 2;
		return 8;
	}
	else if(strcmp(lpszOp, "&&") == 0)
	{
		nOpCount = 2;
		return 3;
	}
	else if(strcmp(lpszOp, "||") == 0)
	{
		nOpCount = 2;
		return 2;
	}
	else if(strcmp(lpszOp, "(") == 0 || strcmp(lpszOp, ")") == 0)
	{
		nOpCount = -1; //操作数以实际为准
		return 100; 
	}
	else if(strcmp(lpszOp, "!") == 0)
	{
		nOpCount = 1;
		return 22;
	}
	else if(strcmp(lpszOp, "&") == 0)
	{
		nOpCount = 2;
		return 7;
	}
	else if(strcmp(lpszOp, "^") == 0)
	{
		nOpCount = 2;
		return 6;
	}
	else if(strcmp(lpszOp, "|") == 0)
	{
		nOpCount = 2;
		return 5;
	}

	//不认识的操作符
	ATLASSERT(FALSE);

	nOpCount = 0;
	return 0;
}

int CompareOperatorPriorityA(LPCSTR op1, LPCSTR op2, int& nOpCount)
{
	int op1c = 0, op2c = 0;
	int op1p = GetOperatorPriorityA(op1, op1c);
	int op2p = GetOperatorPriorityA(op2, op2c);

	if(op1p == op2p)
	{
		nOpCount = op1c;
		return 0;
	}
	else if(op1p > op2p)
	{
		nOpCount = op1c;
		return 1;
	}
	else
	{
		nOpCount = op2c;
		return -1;
	}
}

void push_op(LPCSTR lpszOp, std::deque<CStringA>& qOp, std::deque<CStringA>& qOpCalcstra)
{
	printf("op:%s\n", lpszOp);
	if(qOp.empty())
	{
		qOp.push_back(lpszOp);
		return;
	}

	if(strcmp(lpszOp, "(") == 0)
	{
		qOp.push_back(lpszOp);
		return;
	}

	if(strcmp(lpszOp, ")") == 0)
	{
		while(qOp.size() && qOp.back() != "(")
		{
			qOpCalcstra.push_back(qOp.back());
			qOp.pop_back();
		}
		qOp.pop_back(); //弹出'('
	}
	else
	{
		if(qOp.back() == "(")
		{
			qOp.push_back(lpszOp);
			return;
		}
		//比较优先级
		int nOpsum = 0;
		//如果比栈顶的优先级大,直接入栈
		if(CompareOperatorPriorityA(lpszOp, qOp.back(), nOpsum) > 0)
		{
			qOp.push_back(lpszOp);
		}
		else
		{
			//把小于等于lpszOp优先级的操作符统统弹出,入栈qOpCalcstra
			do 
			{
				qOpCalcstra.push_back(qOp.back());
				qOp.pop_back();
				//比较下一个操作符
			} while (qOp.size() && qOp.back() != '(' && CompareOperatorPriorityA(lpszOp, qOp.back(), nOpsum) <= 0);
			qOp.push_back(lpszOp);
		}
	}
}



__int64 CalcCalcstraA(std::deque<CStringA>& qOpCalcstra)
{
	std::deque<__int64> qOpValue;
	while(qOpCalcstra.size())
	{
		char c = *qOpCalcstra.front();
		if(c >= '0' && c <= '9')
		{
			//是数字
			qOpValue.push_back(_atoi64(qOpCalcstra.front()));

		}
		else
		{
			//是运算符的时候,获取运算符的个数
			int nOpsum = 0;
			GetOperatorPriorityA(qOpCalcstra.front(), nOpsum);
			ATLASSERT(nOpsum > 0);
			switch(nOpsum)
			{
			case 2:
				{
					ATLASSERT(qOpValue.size() >= 2);
					if(qOpValue.size() >= 2)
					{
						__int64 right = qOpValue.back();
						qOpValue.pop_back();
						__int64 left = qOpValue.back();
						qOpValue.pop_back();

						qOpValue.push_back(automic_operator<__int64, __int64>(qOpCalcstra.front(), left, right));
					}
				}
				break;
			case 1:
				{
					ATLASSERT(qOpValue.size() >= 1);
					if(qOpValue.size() >= 1)
					{
						__int64 value = qOpValue.back();
						qOpValue.pop_back();

						qOpValue.push_back(automic_operator<__int64, __int64>(qOpCalcstra.front(), value));
					}
				}
				break;
			}
		}

		qOpCalcstra.pop_front();
	}

	ATLASSERT(qOpValue.size() == 1);
	return qOpValue.back();
}

__int64 CalcValueA(LPCSTR lpszEquation)
{
	if(strchr(lpszEquation, ' '))
	{
		//不能含有空格(为了方便解析)
		ATLASSERT(FALSE);
		return 0;
	}
	//预处理是把+-正负号统一转化为加减操作,方便下面的解析
	//先预处理把类似2*-3,2*+2的处理成2*(0 -3),2*(0 +2),方便解析,否则,区分减号或负号很麻烦
	CStringA strEquation;

	//需要做预处理
	BOOL bProcess = FALSE;
	const char* test = lpszEquation;
	while (*test)
	{
		if(*test == '-' || *test == '+')
		{
			if(test == lpszEquation)
			{
				bProcess = TRUE;
				strEquation.Append("(0");
				//查找数字结尾
				const char* num_end = test + 1;
				while (*num_end)
				{
					if(*num_end >= '0' && *num_end <= '9')
						num_end++;
					else
					{
						//数字到末尾了
						strEquation.Append(test, num_end - test);
						strEquation.AppendChar(')');
						test = num_end;
						break;
					}
				}

				if(*num_end == '\0')
				{
					//上面没有找到,文字到末尾了
					strEquation.Append(test);
					strEquation.AppendChar(')');
					break; //break while (*test)
				}
			}
			else if(test - 1 >= lpszEquation)
			{
				//'+'‘-’前面跟的不是数字,而是其他运算符
				if((*(test - 1) < '0' || *(test - 1) > '9') && (*(test - 1) != ')'))
				{
					if(strEquation.IsEmpty())
					{
						bProcess = TRUE;
						strEquation.Append(lpszEquation, test - lpszEquation);
					}

					strEquation.Append("(0");
					//查找数字结尾
					const char* num_end = test + 1;
					while (*num_end)
					{
						if(*num_end >= '0' && *num_end <= '9')
							num_end++;
						else
						{
							//数字到末尾了
							strEquation.Append(test, num_end - test);
							strEquation.AppendChar(')');
							test = num_end;
							break;
						}
					}

					if(*num_end == '\0')
					{
						//上面没有找到,文字到末尾了
						strEquation.Append(test);
						strEquation.AppendChar(')');
						break; //break while (*test)
					}
				}
			}
		}

		if(bProcess)
		{
			strEquation.AppendChar(*test);
		}

		test++;
	}

	if(bProcess)
		lpszEquation = strEquation.GetString();

	std::deque<CStringA> qOp;
	std::deque<CStringA> qOpCalcstra;
	//拆分操作数(数字)与运算符(非数字)
	const char* cursor = lpszEquation;
	const char* first = cursor;
	while (*cursor)
	{
		if(*cursor == '(' || *cursor == ')' || (*cursor == '!' && *(cursor + 1) != '='))
		{
			if(first < cursor)
			{
				if(*first >= '0' && *first <= '9')
				{
					CStringA strOpValue;
					strOpValue.Append(first, cursor - first);
					qOpCalcstra.push_back(strOpValue);
					printf("opValue:%s\n", strOpValue);
				}
				else
				{
					//操作符解析完毕
					CStringA strOp;
					strOp.Append(first, cursor - first);
					push_op(strOp, qOp, qOpCalcstra);
				}
			}

			CStringA strOp;
			strOp.Append(cursor, 1);
			push_op(strOp, qOp, qOpCalcstra);
			cursor++;
			first = cursor;

			continue;
		}

		//当前是数字
		if(*cursor >= '0' && *cursor <= '9')
		{
			if(*first < '0' || *first > '9')
			{
				//操作符解析完毕
				CStringA strOp;
				strOp.Append(first, cursor - first);
				push_op(strOp, qOp, qOpCalcstra);
				first = cursor;
			}
		}
		else //当前是非数字
		{
			//first记录的是数字,表面操作数解析完毕
			if(*first >= '0' && *first <= '9')
			{
				CStringA strOpValue;
				strOpValue.Append(first, cursor - first);
				qOpCalcstra.push_back(strOpValue);
				first = cursor;
				printf("opValue:%s\n", strOpValue);
			}
		}
		cursor++;
	}

	if(*first >= '0' && *first <= '9')
	{
		CStringA strOpValue;
		strOpValue.Append(first, cursor - first);
		qOpCalcstra.push_back(strOpValue);
		printf("opValue:%s\n", strOpValue);
	}
	else if(*first)
	{
		//操作符解析完毕
		CStringA strOp;
		strOp.Append(first, cursor - first);
		push_op(strOp, qOp, qOpCalcstra);
	}

	while(qOp.size())
	{
		qOpCalcstra.push_back(qOp.back());
		qOp.pop_back();
	}

	//此时qOpCalcstra保存的是逆波兰表达式,根据表达式计算结果
	return CalcCalcstraA(qOpCalcstra);
}

//calcstrvalue.h
#pragma once

#include 

extern __int64 CalcValueA(LPCSTR lpszEquation);
template <class T_VALUE, class T_RESULT>
class automic_operator
{
public:
	operator T_RESULT()
	{
		return m_Result;
	}

	automic_operator(LPCSTR lpszOp, const T_VALUE& Left, const T_VALUE& Right)
	{
		ZeroMemory(&m_Result, sizeof(m_Result));
		if(strcmp(lpszOp, "+") == 0)
		{
			m_Result = Left + Right;
		}
		else if(strcmp(lpszOp, "-") == 0)
		{
			m_Result = Left - Right;
		}
		else if(strcmp(lpszOp, "*") == 0)
		{
			m_Result = Left * Right;
		}
		else if(strcmp(lpszOp, "/") == 0)
		{
			if(Right != 0)
			{
				m_Result = Left / Right;
			}
		}
		else if(strcmp(lpszOp, ">") == 0)
		{
			m_Result = Left > Right;
		}
		else if(strcmp(lpszOp, "<") == 0)
		{
			m_Result = Left < Right;
		}
		else if(strcmp(lpszOp, "==") == 0)
		{
			m_Result = Left == Right;
		}
		else if(strcmp(lpszOp, "!=") == 0)
		{
			m_Result = Left != Right;
		}
		else if(strcmp(lpszOp, ">=") == 0)
		{
			m_Result = Left >= Right;
		}
		else if(strcmp(lpszOp, "<=") == 0)
		{
			m_Result = Left <= Right;
		}
		else if(strcmp(lpszOp, "&&") == 0)
		{
			m_Result = Left && Right;
		}
		else if(strcmp(lpszOp, "||") == 0)
		{
			m_Result = Left || Right;
		}
		else if(strcmp(lpszOp, "&") == 0)
		{
			m_Result = Left & Right;
		}
		else if(strcmp(lpszOp, "^") == 0)
		{
			m_Result = Left ^ Right;
		}
		else if(strcmp(lpszOp, "|") == 0)
		{
			m_Result = Left | Right;
		}
	}

	automic_operator(LPCSTR lpszOp, const T_VALUE& Value)
	{
		ZeroMemory(&m_Result, sizeof(m_Result));
		if(strcmp(lpszOp, "!") == 0)
		{
			m_Result = !Value;
		}
	}

	~automic_operator()
	{

	}

protected:
	T_RESULT m_Result;
};

测试

printf("%d\n", 2&1 || 2 | 3 && 1 ^ 3);

以上实现的是整数的数值运算和逻辑运算。以后咱们讲解通过改写上面的代码,支持浮点数和字符串。
谢谢!

你可能感兴趣的:(c/c++/vc技术,脚本引擎)