系列入口:
脚本的结构与C语言类似,由语句和表达式构成,后来增加了子函数,子函数当作独立的脚本。
本文介绍表达式。从本文开始前面介绍过的东西和后面要介绍的东西会纠缠在一起,互相递归。
目录
一、表达式概览
//表达式,单目算符有两个操作数则为后缀(第一个不使用),函数参数个数不限
//VARIABLE:pVariable 包含常量
//PLUGIN:pPlugin(operands)
//OPERATION:op operands
struct Expression
{
enum types { NULLEXPRESSION, CONSTANT, DEFINE, VARIABLE, FUNCTION, PLUGIN, OPERATION };
types type;
Variable::types result_type;//编译用的结果类型,执行时实际上并不需要这个
size_t source_start;//起始位置
size_t source_end;//结束位置
//变量
string VariableName;
Variable m_variable;//用于常量或运行时创建变量
//函数
CScript * pFunction;//函数索引
//插件
CPluginMap::HANDLE pPlugin;
//插件或函数的参数
vector<Expression > ParamList;
//操作
string op;
vector<Expression > Operands;//操作数
//函数和属性
void* user_p;//用户自行使用的指针,编译时可设置,执行时不可修改(指向的内容由用户决定)
Expression() :type(NULLEXPRESSION), result_type(Variable::NULLVARIABLE), source_start(0), source_end(0), pFunction(NULL), user_p(NULL) {}
Expression* pLeftOperand() { return (Operands.size() >= 1 && Operands[0].type != NULLEXPRESSION ? &Operands[0] : NULL); }//左操作数
Expression const* pLeftOperand()const { return (Operands.size() >= 1 && Operands[0].type != NULLEXPRESSION ? &Operands[0] : NULL); }//左操作数
Expression* pRightOperand() { return (Operands.size() >= 2 && Operands[1].type != NULLEXPRESSION ? &Operands[1] : NULL); }//右操作数
Expression const* pRightOperand()const { return (Operands.size() >= 2 && Operands[1].type != NULLEXPRESSION ? &Operands[1] : NULL); }//右操作数
bool AddLeftOperand(Expression const& exp)
{
if (NULL != pLeftOperand())return false;
if (Operands.size() < 1)Operands.resize(1);
Operands[0] = exp;
return true;
}
bool AddRightOperand(Expression const& exp)
{
if (NULL != pRightOperand())return false;
if (Operands.size() < 2)Operands.resize(2);
Operands[1] = exp;
return true;
}
Variable::types GetResultType(string& source, T_VARIABLE_S& vars);
//setpp记录已经做过的++ --
bool CheckExpression(string& source, T_VARIABLE_S& vars, long level, set<string >& setpp);
bool ExecExpression(CScript const& script, T_VARIABLE_S& vars, Variable& ret, void* pe)const;
string ToString(string const& source, T_VARIABLE_S const& vars, long level = 0)const
{
STATIC_C const char typestr[][TOKEN_BUF_LEN] = { "NULLEXPRESSION","DEFINE","CONSTANT","VARIABLE","FUNCTION","PLUGIN","OPERATION" };//必须与types对应
char buf[256];
string ret;
size_t i;
string prefix;
prefix.assign(level * 4, ' ');
ret = prefix.c_str();
sprintf(buf, "%03ld %03ld 表达式类型 %-12s 结果类型 %-12s: ", source_start, source_end - 1, typestr[type], Variable::TypeStr(result_type));
ret += buf;
ret += source.substr(source_start, source_end - source_start);
ret += "\r\n";
switch (type)
{
case DEFINE:
ret += VariableName + " " + m_variable.ToString(level + 1);
ret += "\r\n";
break;
case CONSTANT:
ret += m_variable.ToString(level + 1);
ret += "\r\n";
break;
case VARIABLE:
if (NULL == vars.FindVariable(VariableName))
{
ret += VariableName;
}
else ret += vars.FindVariable(VariableName)->ToString(level + 1);
ret += "\r\n";
break;
case FUNCTION:
ret += prefix + "函数名: " + pFunction->script_name;
if (ParamList.size() != 0)
{
ret += prefix + "参数:\r\n";
for (i = 0; i < ParamList.size(); ++i)
{
ret += ParamList[i].ToString(source, vars, level + 1);
}
}
else
{
ret += "\r\n";
}
break;
case PLUGIN:
ret += prefix + "插件名: " + CPluginMap::GetPlugin(pPlugin)->plugin_name;
if (ParamList.size() != 0)
{
ret += prefix + "参数:\r\n";
for (i = 0; i < ParamList.size(); ++i)
{
ret += ParamList[i].ToString(source, vars, level + 1);
}
}
else
{
ret += "\r\n";
}
break;
case OPERATION:
if (NULL != pLeftOperand())
{
ret += pLeftOperand()->ToString(source, vars, level + 1);
}
ret += prefix + " " + op;
ret += "\r\n";
if (NULL != pRightOperand())
{
ret += pRightOperand()->ToString(source, vars, level + 1);
}
break;
default:
break;
}
return ret;
}
};
表达式就是用运算符穿起来的一组变量,总是可以拆解为“左操作数+运算符+右操作数”的形式(三元运算符没打算支持),比如“a+b+c”就可以拆解为:
----a+表达式
--------b+c
表达式总是有个值的,除非是函数调用,返回值为“void”。
二、表达式类型
enum types { NULLEXPRESSION, CONSTANT, DEFINE, VARIABLE, FUNCTION, PLUGIN, OPERATION };
这个枚举定义了表达式类型,包括:
- 无类型 空的表达式,只是表达式结构的初始状态
- 常量
- 定义 定义变量
- 变量
- 函数 脚本里面定义的子函数
- 插件 外部实现的自定义函数(包括内置函数)
- 运算
实际上这是一个递归的表达方式,一个运算最终会被拆解到变量、常量和函数,函数又会被拆解到插件和参数(每个参数是一个表达式)。
三、成员变量
表达式具有如下几个通用成员变量:
类型 | 名称 | 用途 |
types | type | 类型 |
Variable::types | result_type | 结果类型,仅在编译时使用 |
size_t | source_start | 在脚本中的起始位置 |
size_t | source_end | 在脚本中的结束位置 |
其余参数视类型不同而分别使用。比如变量使用VariableName和m_variable,操作则使用op和Operands。函数和插件都要用到ParamList。
user_p并没有实际使用。
四、计算结果类型
Variable::types GetResultType(string& source, T_VARIABLE_S& vars)
{
Variable::types _resulttype = Variable::NULLVARIABLE;
bool eva;
switch (type)
{
case NULLEXPRESSION:
_resulttype = Variable::NULLVARIABLE;
break;
case DEFINE:
_resulttype = m_variable.type;
break;
case CONSTANT:
_resulttype = m_variable.type;
break;
case VARIABLE:
if (0 == VariableName.size())CException::Throw(__FILE__, __LINE__, source, source_start, "未声明的变量");
if (NULL == vars.FindVariable(VariableName))
{
cout << VariableName << endl << vars.ToString() << endl;
CException::Throw(__FILE__, __LINE__, source, source_start, "未声明的变量");
}
_resulttype = vars.FindVariable(VariableName)->type;
break;
case FUNCTION:
if (NULL== pFunction)CException::Throw(__FILE__, __LINE__, source, source_start, "未声明的函数");
_resulttype = pFunction->return_type;
break;
case PLUGIN:
if (pPlugin.isNULL())CException::Throw(__FILE__, __LINE__, source, source_start, "未声明的插件");
_resulttype = CPluginMap::GetPlugin(pPlugin)->plugin_return_type;
break;
case OPERATION:
if (NULL == pLeftOperand() && NULL != pRightOperand())_resulttype = pRightOperand()->GetResultType(source, vars);
else if (NULL != pLeftOperand() && NULL == pRightOperand())_resulttype = pLeftOperand()->GetResultType(source, vars);
else if (NULL != pLeftOperand() && NULL != pRightOperand())
{
if ("=" == op || "+=" == op || "-=" == op || "*=" == op || "/=" == op || "%=" == op)eva = true;
else eva = false;
_resulttype = Variable::typeUpgrade(pLeftOperand()->GetResultType(source, vars), pRightOperand()->GetResultType(source, vars), eva);
if (">" == op || "<" == op || ">=" == op || "<=" == op || "==" == op || "!=" == op || "||" == op || "&&" == op)
{
if (Variable::NULLVARIABLE != _resulttype)_resulttype = Variable::LONG;
}
if ("," == op)_resulttype = pRightOperand()->GetResultType(source, vars);
}
else CException::Throw(__FILE__, __LINE__, source, source_start, "左右操作数不能都没有");
break;
default:
CException::Throw(__FILE__, __LINE__, source, source_start, "未知的表达式类型");
break;
}
return _resulttype;
}
计算结果类型根据表达式类型不同而有不同的方法,左右操作数根据运算符得到结果类型。
五、编译时检查
//setpp记录已经做过的++ --
bool CheckExpression(string& source, T_VARIABLE_S& vars, long level, set<string >& setpp)
{
size_t i;
string msg;
Variable var;
vector<Variable > _paramvars;//函数调用的参数
if (DEFINE == type)
{
if (!vars.AddVariable(VariableName, m_variable))CException::Throw(__FILE__, __LINE__, source, source_start, "添加变量失败");
//cout <<"添加变量 "<< VariableName << endl << vars.ToString() << endl;
}
result_type = GetResultType(source, vars);
switch (type)
{
case NULLEXPRESSION:
break;
case DEFINE:
if (0 == VariableName.size())CException::Throw(__FILE__, __LINE__, source, source_start, "未声明的变量");
if (Variable::NULLVARIABLE == m_variable.type)CZException::Throw(__FILE__, __LINE__, source, source_start, "变量类型不能为NULLVARIABLE");
if (Operands.size() > 1)CException::Throw(__FILE__, __LINE__, source, source_start, "变量定义最多有一个操作数");
break;
case CONSTANT:
if (0 != VariableName.size())CException::Throw(__FILE__, __LINE__, source, source_start, "常量不应该有名字");
if (Variable::NULLVARIABLE == m_variable.type)CException::Throw(__FILE__, __LINE__, source, source_start, "常量类型不能为NULLVARIABLE");
break;
case VARIABLE:
if (0 == VariableName.size())CException::Throw(__FILE__, __LINE__, source, source_start, "未声明的变量");
break;
case FUNCTION:
if (NULL==pFunction)CException::Throw(__FILE__, __LINE__, source, source_start, "未声明的函数");
if (!pFunction->IsCompiled())CException::Throw(__FILE__, __LINE__, source, source_start, "子函数未能成功编译");
if (pFunction->m_EnvVariables.size() != ParamList.size())CException::Throw(__FILE__, __LINE__, source, source_start, "函数参数个数错误");
for (i = 0; i < ParamList.size(); ++i)
{
ParamList[i].CheckExpression(source, vars, 0, setpp);
}
break;
case PLUGIN:
if (pPlugin.isNULL())CException::Throw(__FILE__, __LINE__, source, source_start, "未声明的插件");
_paramvars.reserve(ParamList.size());
for (i = 0; i < ParamList.size(); ++i)
{
ParamList[i].CheckExpression(source, vars, 0, setpp);
if (Expression::VARIABLE == ParamList[i].type && vars.FindVariable(ParamList[i].VariableName)->isconst)
{
_paramvars.push_back(*vars.FindVariable(ParamList[i].VariableName));
}
else
{
var.type = ParamList[i].result_type;
_paramvars.push_back(var);
}
}
if (!CPluginMap::GetPlugin(pPlugin)->CheckPlugin(_paramvars, user_p, msg))CException::Throw(__FILE__, __LINE__, source, source_start, msg.c_str());
break;
case OPERATION:
//先检查左右表达式
if (NULL != pLeftOperand())
{
pLeftOperand()->CheckExpression(source, vars, level + 1, setpp);
}
if (NULL != pRightOperand())
{
pRightOperand()->CheckExpression(source, vars, level + 1, setpp);
}
//检查操作是否合理
if (NULL == pRightOperand())
{//后缀
if ("++" == op || "--" == op)
{
if (result_type != Variable::LONG)CException::Throw(__FILE__, __LINE__, source, source_start, "后缀++ --操作只能用于整数");
if (Expression::VARIABLE != pLeftOperand()->type || vars.FindVariable(pLeftOperand()->VariableName)->isconst)CException::Throw(__FILE__, __LINE__, source, source_start, "后缀++ --操作只能用于变量");
if (0 != level)CException::Throw(__FILE__, __LINE__, source, source_start, "后缀++ --操作只能作为独立语句使用,如'i++;'或'for(...;...;i++)',不能用于赋值或组合运算");
}
else
{
CException::Throw(__FILE__, __LINE__, source, source_start, "不支持的后缀操作");
}
}
else if (NULL == pLeftOperand())
{//前缀
if ("-" == op || "+" == op)
{
if (result_type != Variable::DOUBLE && result_type != Variable::LONG)CException::Throw(__FILE__, __LINE__, source, source_start, "前缀+-操作只能用于数值");
}
else if ("!" == op)
{
if (result_type != Variable::LONG)CException::Throw(__FILE__, __LINE__, source, source_start, "前缀!操作只能用于整数");
}
else if ("++" == op || "--" == op)
{
if (result_type != Variable::LONG)CException::Throw(__FILE__, __LINE__, source, source_start, "前缀++ --操作只能用于整数");
if (Expression::VARIABLE != pRightOperand()->type || vars.FindVariable(pRightOperand()->VariableName)->isconst)CException::Throw(__FILE__, __LINE__, source, source_start, "前缀++ --操作只能用于变量");
if (setpp.find(pRightOperand()->VariableName) != setpp.end())
CException::Throw(__FILE__, __LINE__, source, source_start, "前缀++ --操作每个语句对每个变量仅限使用一次(不区分++ --)");
setpp.insert(pRightOperand()->VariableName);
}
else
{
CException::Throw(__FILE__, __LINE__, source, source_start, "不支持的前缀操作");
}
}
else
{//二元
if ("+" == op || ">" == op || "<" == op || ">=" == op || "<=" == op || "==" == op || "!=" == op || "," == op)
{
}
else if ("-" == op || "*" == op || "/" == op)
{
if (Variable::STRING == result_type)CException::Throw(__FILE__, __LINE__, source, source_start, "不能对字符串做-*/");
}
else if ("-=" == op || "*=" == op || "/=" == op)
{
if (Variable::STRING == result_type)CException::Throw(__FILE__, __LINE__, source, source_start, "不能对字符串做-*/");
if (pLeftOperand()->type != Expression::VARIABLE || vars.FindVariable(pLeftOperand()->VariableName)->isconst)CException::Throw(__FILE__, __LINE__, source, source_start, "不能对常量赋值");
}
else if ("&&" == op || "||" == op)
{
if (Variable::STRING == result_type)CException::Throw(__FILE__, __LINE__, source, source_start, "不能对字符串做&& ||");
}
else if ("%" == op)
{
if (Variable::LONG != result_type)CException::Throw(__FILE__, __LINE__, source, source_start, "只能对整数做%");
}
else if ("%=" == op)
{
if (Variable::LONG != result_type)CException::Throw(__FILE__, __LINE__, source, source_start, "只能对整数做%");
if (pLeftOperand()->type != Expression::VARIABLE || vars.FindVariable(pLeftOperand()->VariableName)->isconst)CException::Throw(__FILE__, __LINE__, source, source_start, "不能对常量赋值");
}
else if ("=" == op || "+=" == op)
{
if (pLeftOperand()->type != Expression::VARIABLE || vars.FindVariable(pLeftOperand()->VariableName)->isconst)CException::Throw(__FILE__, __LINE__, source, source_start, "不能对常量赋值");
}
else
{
CException::Throw(__FILE__, __LINE__, source, source_start, (op + " 不支持的操作").c_str());
}
if ("," != op)
{
if (Variable::NULLVARIABLE == result_type)CException::Throw(__FILE__, __LINE__, source, source_start, "操作符两边类型不匹配,不允许数值和字符串的自动转换");
}
}
break;
}
return true;
}
这个函数在编译后检查是否存在语法问题。如果检查成功,起码在语法上是没有问题的。
六、执行
bool ExecExpression(CZBScript const& script, T_VARIABLE_S& vars, Variable& ret, void* pe)const
{
size_t i;
string msg;
vector<Variable > _paramvars;//插件调用的参数
Variable ret_left;
Variable ret_right;
ret.type = result_type;
switch (type)
{
case NULLEXPRESSION:
ret.type = Variable::NULLVARIABLE;
break;
case DEFINE:
ret.type = Variable::NULLVARIABLE;
vars.AddVariable(VariableName, m_variable);
if (NULL != pLeftOperand())
{
pLeftOperand()->ExecExpression(script, vars, ret_left, pe);
if (ret_left.isNull())script.Throw(__FILE__, __LINE__, "左操作数计算出错");
ret = *(vars.FindVariable(VariableName)) = ret_left;
}
break;
case CONSTANT:
ret = m_variable;
break;
case VARIABLE:
ret = *vars.FindVariable(VariableName);
break;
case FUNCTION:
{
T_VARIABLE_BLOCK tmpparams;
tmpparams = pFunction->m_EnvVariables;
if(tmpparams.size()!= ParamList.size())script.Throw(__FILE__, __LINE__, "函数参数个数错误");
for (i = 0; i < ParamList.size(); ++i)
{
ParamList[i].ExecExpression(script, vars, ret, pe);
tmpparams[i].second = ret;
}
T_VARIABLE_S tmpvars;
tmpvars.FromParentVars(vars, pFunction->count_global_variable, &tmpparams);
ret.type = result_type;
if (!pFunction->ExeSentences(tmpvars, ret, pe))script.Throw(__FILE__, __LINE__, "函数执行出错");
}
break;
case PLUGIN:
_paramvars.reserve(ParamList.size());
for (i = 0; i < ParamList.size(); ++i)
{
ParamList[i].ExecExpression(script, vars, ret, pe);
_paramvars.push_back(ret);
}
ret.type = result_type;
if (!CPluginMap::GetPlugin(pPlugin)->ExecFunction(_paramvars, user_p, ret, msg, pe))script.Throw(__FILE__, __LINE__, "插件执行出错");
break;
case OPERATION:
//计算
if (NULL == pRightOperand())
{
if ("++" == op)
{
ret = *vars.FindVariable(pLeftOperand()->VariableName);
++vars.FindVariable(pLeftOperand()->VariableName)->lValue;
}
else if ("--" == op)
{
ret = *vars.FindVariable(pLeftOperand()->VariableName);
--vars.FindVariable(pLeftOperand()->VariableName)->lValue;
}
else
{
script.Throw(__FILE__, __LINE__, "不支持的前缀操作");
}
}
else if (NULL == pLeftOperand())
{//前缀
pRightOperand()->ExecExpression(script, vars, ret_right, pe);
if (ret_right.isNull())script.Throw(__FILE__, __LINE__, "右操作数计算出错");
if ("-" == op)
{
ret = -ret_right;
}
else if ("+" == op)
{
ret = ret_right;
}
else if ("++" == op)
{
++vars.FindVariable(pRightOperand()->VariableName)->lValue;
ret = *vars.FindVariable(pRightOperand()->VariableName);
}
else if ("--" == op)
{
--vars.FindVariable(pRightOperand()->VariableName)->lValue;
ret = *vars.FindVariable(pRightOperand()->VariableName);
}
else if ("!" == op)
{
ret = ret_right;
ret.lValue = (0 == ret.lValue ? 1 : 0);
}
else
{
script.Throw(__FILE__, __LINE__, "不支持的前缀操作");
}
}
else
{//二元
if ("||" == op)
{//若前面为真则不计算后面
pLeftOperand()->ExecExpression(script, vars, ret_left, pe);
if (ret_left.isNull())script.Throw(__FILE__, __LINE__, "左操作数计算出错");
if (ret_left.GetBool())ret = ret_left;
else
{
pRightOperand()->ExecExpression(script, vars, ret_right, pe);
if (ret_right.isNull())script.Throw(__FILE__, __LINE__, "右操作数计算出错");
ret = ret_right;
}
}
else
{
{
//先计算左右表达式
pLeftOperand()->ExecExpression(script, vars, ret_left, pe);
if (ret_left.isNull())script.Throw(__FILE__, __LINE__, "左操作数计算出错");
pRightOperand()->ExecExpression(script, vars, ret_right, pe);
if (ret_right.isNull())script.Throw(__FILE__, __LINE__, "右操作数计算出错");
else if ("+" == op)ret = ret_left + ret_right;
else if ("-" == op)ret = ret_left - ret_right;
else if ("*" == op)ret = ret_left * ret_right;
else if ("/" == op)ret = ret_left / ret_right;
else if ("%" == op)ret = ret_left % ret_right;
else if (">" == op)ret = ret_left > ret_right;
else if ("<" == op)ret = ret_left < ret_right;
else if (">=" == op)ret = ret_left >= ret_right;
else if ("<=" == op)ret = ret_left <= ret_right;
else if ("==" == op)ret = ret_left == ret_right;
else if ("!=" == op)ret = ret_left != ret_right;
else if ("&&" == op)ret = ret_left && ret_right;
else if ("=" == op)ret = *(vars.FindVariable(pLeftOperand()->VariableName)) = ret_right;
else if ("+=" == op)ret = *(vars.FindVariable(pLeftOperand()->VariableName)) = ret_left + ret_right;
else if ("-=" == op)ret = *(vars.FindVariable(pLeftOperand()->VariableName)) = ret_left - ret_right;
else if ("*=" == op)ret = *(vars.FindVariable(pLeftOperand()->VariableName)) = ret_left * ret_right;
else if ("/=" == op)ret = *(vars.FindVariable(pLeftOperand()->VariableName)) = ret_left / ret_right;
else if ("%=" == op)ret = *(vars.FindVariable(pLeftOperand()->VariableName)) = ret_left % ret_right;
else if ("," == op)
{
ret = ret_right;
}
else
{
script.Throw(__FILE__, __LINE__, "不支持的操作");
}
}
}
if (Variable::NULLVARIABLE == ret.type)script.Throw(__FILE__, __LINE__, "操作符两边类型不匹配");
}
break;
}
return true;
}
执行时仍然可能出错。
(这里是结束,但不是整个系列的结束)