1358 lines
31 KiB
C++
1358 lines
31 KiB
C++
#include "dl_script.h"
|
||
|
||
#include <cassert>
|
||
|
||
#include "io/dl_log.h"
|
||
#include "base/dl_string.h"
|
||
|
||
namespace dl
|
||
{
|
||
enum class ScriptLogType
|
||
{
|
||
E_print, // 调用打印
|
||
E_debug,
|
||
E_info,
|
||
E_err, // 用户错误
|
||
E_damage, // 引擎bug
|
||
E_compile,
|
||
};
|
||
|
||
void script_print(ScriptLogType type, std::string_view str)
|
||
{
|
||
switch (type)
|
||
{
|
||
case ScriptLogType::E_print:
|
||
log_info("script:{}", str);
|
||
break;
|
||
case ScriptLogType::E_debug:
|
||
log_msg("script:【调试】 {}", str);
|
||
break;
|
||
case ScriptLogType::E_info:
|
||
log_msg("script:【提示】 {}", str);
|
||
break;
|
||
case ScriptLogType::E_err:
|
||
log_warn("script:【错误】 {}", str);
|
||
break;
|
||
case ScriptLogType::E_damage:
|
||
log_err("script:【严重错误】 {}", str);
|
||
break;
|
||
case ScriptLogType::E_compile:
|
||
log_err("script:【编译错误】 {}", str);
|
||
break;
|
||
default:
|
||
assert(0);
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
ScriptBlock ScriptSystem::Compile(std::string_view src_raw)
|
||
{
|
||
script_print(ScriptLogType::E_debug, fmt::format("{:=^32}", "开始编译脚本"));
|
||
script_print(ScriptLogType::E_debug, fmt::format("源:\n{}", src_raw));
|
||
|
||
ScriptBlock ret;
|
||
|
||
// 预处理
|
||
g_script->PreprocessLineNumber(ret, src_raw);
|
||
std::string src = g_script->Preprocess(src_raw);
|
||
|
||
|
||
std::vector<ScriptSegment>& vec_seg = ret._allSeg;
|
||
|
||
std::size_t beg = 0;
|
||
std::size_t end = 0;
|
||
std::vector<ScriptToken> vec_token;
|
||
|
||
// 括号层级
|
||
size_t level_brace = 0;
|
||
// 行号
|
||
size_t line_number = 0;
|
||
|
||
enum class State
|
||
{
|
||
FREE,// 空闲
|
||
NORMAL,// 单词匹配
|
||
STRING,// 字符串
|
||
SYMBOL,// 符号
|
||
};
|
||
State state = State::FREE;
|
||
|
||
// 构造当前
|
||
auto fn_string = [&]() {
|
||
return std::string_view{ src.begin() + beg, src.begin() + end };
|
||
};
|
||
// 构造word 返回值 0正常、1失败
|
||
auto fn_make_word = [&](ScriptToken::Type type) -> int {
|
||
std::string_view word = fn_string();
|
||
if (!word.empty())
|
||
{
|
||
if (type == ScriptToken::Type::OPERATOR)
|
||
{
|
||
// 操作符拆分、例如 ++-( 拆分为 ++、-、(
|
||
size_t beg = 0;
|
||
size_t end = word.size();
|
||
while (beg != end)
|
||
{
|
||
std::string_view word_sub{ word.data() + beg, word.data() + end };
|
||
if (g_script->IsOperator(word_sub))
|
||
{
|
||
script_print(ScriptLogType::E_debug,
|
||
fmt::format("操作符:[{}]", word_sub));
|
||
vec_token.push_back({ type, word_sub });
|
||
beg = end;
|
||
end = word.size();
|
||
}
|
||
else
|
||
{
|
||
if (word_sub.size() == 1)
|
||
{
|
||
script_print(ScriptLogType::E_compile,
|
||
fmt::format("无效操作符:{}中的{}", word, word_sub));
|
||
return 1;
|
||
}
|
||
--end;
|
||
}
|
||
}
|
||
}
|
||
else if(type == ScriptToken::Type::STRING)
|
||
{
|
||
script_print(ScriptLogType::E_debug, fmt::format("字符串:[\"{}\"]", word));
|
||
vec_token.push_back({ type, word });
|
||
}
|
||
else
|
||
{
|
||
script_print(ScriptLogType::E_debug, fmt::format("token :[{}]", word));
|
||
vec_token.push_back({ type, word });
|
||
}
|
||
}
|
||
return 0;
|
||
};
|
||
// 表达式完成
|
||
auto fn_expr = [&](ScriptSegment& seg) {
|
||
if (vec_token.empty())
|
||
{
|
||
script_print(ScriptLogType::E_debug, "空行");
|
||
return;
|
||
}
|
||
|
||
seg._allExpr.push_back({});
|
||
|
||
ScriptExpr& expr = seg._allExpr.back();
|
||
expr._lineNumber = line_number;
|
||
expr._strRaw = ret._vecSource[expr._lineNumber];
|
||
|
||
|
||
// 例:print a + b
|
||
|
||
// 1、函数视为变量
|
||
// 2、若变量后不是操作符,则添加 call 视为函数调用
|
||
// 3、检查是否合法中缀表达式
|
||
size_t i = 0;
|
||
std::vector<ScriptExprToken> vec_mid;
|
||
|
||
// token 转 表达式
|
||
for (ScriptToken& token : vec_token)
|
||
{
|
||
ScriptExprToken expr = MakeExprToken(ret, token);
|
||
vec_mid.push_back(expr);
|
||
}
|
||
vec_token.clear();
|
||
|
||
// 4、转为后缀表达式
|
||
|
||
// 初始化:创建一个空栈用于存放运算符,以及一个空列表或向量用于存放最终的后缀表达式。
|
||
std::vector<ScriptExprToken>& vec_out = expr._allToken;
|
||
|
||
vec_out = ConvertMid(vec_mid);
|
||
|
||
|
||
// 打印
|
||
script_print(ScriptLogType::E_debug, "后缀表达式为:");
|
||
std::string str;
|
||
for (ScriptExprToken& expr : vec_out)
|
||
{
|
||
if(expr._type == ScriptExprTokenType::ESET_literal
|
||
&& expr._value.GetType() == ScriptVarType::E_string)
|
||
str += String::AddQuotes(expr._name);
|
||
else
|
||
str += expr._name;
|
||
str += ' ';
|
||
}
|
||
if (!str.empty())
|
||
str.pop_back();
|
||
script_print(ScriptLogType::E_debug, str);
|
||
|
||
|
||
script_print(ScriptLogType::E_debug,
|
||
fmt::format("表达式完成,词数量 {}", vec_out.size()));
|
||
};
|
||
size_t seg_level = 0;
|
||
ScriptSegment seg;
|
||
auto fn_segment = [&]() {
|
||
seg._level = seg_level;
|
||
vec_seg.push_back(seg);
|
||
script_print(ScriptLogType::E_debug,
|
||
fmt::format("==========代码段完成,表达式数量 {}", seg._allExpr.size()));
|
||
seg._allExpr.clear();
|
||
};
|
||
while (true)
|
||
{
|
||
// end为当前字符
|
||
char ch = src[end];
|
||
#ifndef NDEBUG
|
||
std::string_view word_current = fn_string();
|
||
#endif
|
||
switch (state)
|
||
{
|
||
case State::FREE:
|
||
{// 空闲状态,跳过空白字符
|
||
if (ch == ' ' || ch == '\t')
|
||
{
|
||
++beg;
|
||
++end;
|
||
}
|
||
else if (ch == '\'' || ch == '\"')
|
||
{
|
||
state = State::STRING;
|
||
++beg;
|
||
++end;
|
||
}
|
||
else
|
||
{
|
||
state = State::NORMAL;
|
||
}
|
||
}
|
||
break;
|
||
case State::NORMAL:
|
||
{// 普通字符则继续,
|
||
if (ch == ' ' || ch == '\t' || ch == '\n')
|
||
{
|
||
fn_make_word(ScriptToken::Type::NORMAL);
|
||
if (ch == '\n' && level_brace == 0)
|
||
{
|
||
fn_expr(seg); // 解析表达式
|
||
++line_number;
|
||
}
|
||
++end;
|
||
beg = end;
|
||
}
|
||
else if (ch == '\'' || ch == '\"')
|
||
{
|
||
state = State::STRING;
|
||
++beg;
|
||
++end;
|
||
}
|
||
else if (ch == '{')
|
||
{
|
||
if (beg != end)
|
||
{
|
||
fn_make_word(ScriptToken::Type::NORMAL);
|
||
}
|
||
++end;
|
||
beg = end;
|
||
seg._type = ScriptSegmentType::E_expr;
|
||
fn_segment();
|
||
++seg_level;
|
||
state = State::FREE;
|
||
}
|
||
else if (ch == '}')
|
||
{
|
||
if (beg != end)
|
||
{
|
||
fn_make_word(ScriptToken::Type::NORMAL);
|
||
}
|
||
++end;
|
||
beg = end;
|
||
seg._type = ScriptSegmentType::E_expr;
|
||
fn_segment();
|
||
state = State::FREE;
|
||
if (seg_level == 0)
|
||
{
|
||
script_print(ScriptLogType::E_compile, fmt::format("{}", "花括号数量不匹配!"));
|
||
return {};
|
||
}
|
||
--seg_level;
|
||
}
|
||
else if (g_script->IsMaybeOperator(ch))
|
||
{
|
||
if (beg != end)
|
||
{
|
||
fn_make_word(ScriptToken::Type::NORMAL);
|
||
++beg;
|
||
}
|
||
++end;
|
||
state = State::SYMBOL;
|
||
}
|
||
else
|
||
++end;
|
||
}
|
||
break;
|
||
case State::STRING:
|
||
{
|
||
if (ch == '\'' || ch == '\"')
|
||
{
|
||
fn_make_word(ScriptToken::Type::STRING);
|
||
|
||
++end;
|
||
beg = end;
|
||
state = State::FREE;
|
||
}
|
||
else
|
||
++end;
|
||
}
|
||
break;
|
||
case State::SYMBOL:
|
||
{
|
||
if (g_script->IsMaybeOperator(ch))
|
||
{// 连续解析,后面再拆分
|
||
++end;
|
||
}
|
||
else
|
||
{
|
||
fn_make_word(ScriptToken::Type::OPERATOR);
|
||
beg = end;
|
||
state = State::FREE;
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
if (end >= src.size())
|
||
{
|
||
fn_make_word(ScriptToken::Type::NORMAL);
|
||
if(!vec_token.empty())
|
||
fn_expr(seg);
|
||
seg._type = ScriptSegmentType::E_expr;
|
||
fn_segment();
|
||
break;
|
||
}
|
||
}
|
||
|
||
script_print(ScriptLogType::E_debug, fmt::format("{:=^32}", "词法分析完成"));
|
||
|
||
return ret;
|
||
}
|
||
|
||
std::vector<dl::ScriptExprToken> ScriptSystem::ConvertMid(const std::vector<ScriptExprToken>& vec_mid)
|
||
{
|
||
std::vector<ScriptExprToken> vec_out;
|
||
std::stack<ScriptExprToken> s;
|
||
// 遍历中缀表达式:从左到右遍历中缀表达式的每一个元素。
|
||
for (const ScriptExprToken& expr : vec_mid)
|
||
{
|
||
// 如果当前元素是操作数(如数字、变量等非运算符),直接将其添加到后缀表达式的列表中。
|
||
if (expr._type != ScriptExprTokenType::ESET_operator)
|
||
{
|
||
vec_out.push_back(expr);
|
||
}
|
||
else if(expr._name == "(")
|
||
s.push(expr);
|
||
else if (expr._name == ")")
|
||
{
|
||
// 右小括号,出栈一直出到遇到第一个左括号
|
||
while (true)
|
||
{
|
||
ScriptExprToken& expr_top = s.top();
|
||
if (expr_top._name != "(")
|
||
{
|
||
vec_out.push_back(expr_top);
|
||
s.pop();
|
||
}
|
||
else
|
||
{
|
||
s.pop();
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else if(s.empty())
|
||
s.push(expr);
|
||
else
|
||
{
|
||
int p_new = GetOperatorPriority(expr._name);
|
||
|
||
while (true)
|
||
{
|
||
ScriptExprToken& expr_top = s.top();
|
||
if (expr_top._name == "(")
|
||
{
|
||
s.push(expr);
|
||
break;
|
||
}
|
||
int p_top = GetOperatorPriority(expr_top._name);
|
||
if (p_top < p_new)
|
||
{// 如果栈里比较小,则后计算,所以自己入栈
|
||
s.push(expr);
|
||
break;
|
||
}
|
||
else
|
||
{// 否则栈顶送入到结果,先计算
|
||
vec_out.push_back(expr_top);
|
||
s.pop();
|
||
// 弹栈之后,若空了,
|
||
if (s.empty())
|
||
{
|
||
s.push(expr);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// 处理剩余栈内运算符:
|
||
// 遍历结束后,如果栈中还有剩余的运算符,将它们全部弹出并追加到后缀表达式的末尾。
|
||
// 这是因为这些运算符没有遇到更低优先级的运算符,按照运算符的顺序应该在表达式的最后。
|
||
while (!s.empty())
|
||
{
|
||
vec_out.push_back(s.top());
|
||
s.pop();
|
||
}
|
||
|
||
/*注意事项:
|
||
需要定义一个函数来确定每个运算符的优先级。
|
||
空格和括号在实际处理时可能需要特殊考虑,尤其是处理括号时,遇到左括号直接入栈,遇到右括号则弹出栈顶运算符直到遇到左括号对应的符号。
|
||
对于一元运算符,如负号 - 作为负数标志或逻辑非 !,需要特别处理规则,确保正确识别其作用。*/
|
||
|
||
return vec_out;
|
||
}
|
||
|
||
std::string ScriptSystem::Preprocess(std::string_view src)
|
||
{
|
||
std::string ret;
|
||
int mode = 0; // 1.string 2.comment
|
||
for (size_t i = 0; i < src.size(); ++i)
|
||
{
|
||
const char& ch = src[i];
|
||
if (mode == 0)
|
||
{
|
||
if (ch == '\"')
|
||
{
|
||
mode = 1;
|
||
ret.push_back(ch);
|
||
}
|
||
else
|
||
{
|
||
if (ch == '/'
|
||
&& i + 1 < src.size()
|
||
&& src[i + 1] == '/')
|
||
mode = 2;
|
||
else
|
||
ret.push_back(ch);
|
||
}
|
||
}
|
||
else if (mode == 1)
|
||
{
|
||
if(ch == '\"')
|
||
mode = 0;
|
||
ret.push_back(ch);
|
||
}
|
||
else
|
||
{
|
||
if (ch == '\n')
|
||
{
|
||
mode = 0;
|
||
ret.push_back(ch);
|
||
}
|
||
}
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
void ScriptSystem::PreprocessLineNumber(ScriptBlock& block, std::string_view src)
|
||
{
|
||
std::vector<std::string_view> vec = String::SplitView(src, "\n");
|
||
|
||
for (auto& iter : vec)
|
||
block._vecSource.push_back(std::string{ iter });
|
||
/*if (vec.empty())
|
||
return;
|
||
for (size_t i = 0; i < vec.size() - 1; ++i)
|
||
{
|
||
block._vecSource.push_back(vec[i] + "\n");
|
||
}
|
||
block._vecSource.push_back(std::string{ vec.back() });*/
|
||
}
|
||
|
||
bool ScriptSystem::IsOperator(std::string_view str)
|
||
{
|
||
return _allOperator.contains(str);
|
||
}
|
||
|
||
bool ScriptSystem::IsMaybeOperator(char ch)
|
||
{
|
||
return (ch == '!')
|
||
|| (ch >= 35 && ch <= 47)
|
||
|| (ch == ':')
|
||
|| (ch >= 60 && ch <= 64)
|
||
|| (ch >= 91 && ch <= 94)
|
||
|| (ch == '|')
|
||
|| (ch == '~');
|
||
}
|
||
|
||
int ScriptSystem::GetOperatorPriority(std::string_view str)
|
||
{
|
||
auto iter = _allOperator.find(str);
|
||
if (iter == _allOperator.end())
|
||
return -1;
|
||
return iter->second;
|
||
}
|
||
|
||
bool ScriptSystem::IsKeyword(std::string_view str)
|
||
{
|
||
return _allKeyword.contains(str);
|
||
}
|
||
|
||
bool ScriptSystem::IsVariable(std::string_view str)
|
||
{
|
||
/*变量名称只能使用字母、数字和下划线。
|
||
|
||
第一个字符必须是字母或下划线,因为C++不允许数字作为变量名的首字符。
|
||
|
||
区分大小写,即变量name和Name是不同的。
|
||
|
||
避免使用C++的关键字和保留字作为变量名。*/
|
||
assert(!str.empty());
|
||
if (!std::isalpha(str[0]) && str[0] != '_')
|
||
return false;
|
||
|
||
for (auto& iter : str)
|
||
{
|
||
if (iter != '_'
|
||
&& !std::isalpha(iter)
|
||
&& !std::isdigit(iter))
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
bool ScriptSystem::IsGlobalVariable(std::string_view str)
|
||
{
|
||
return str.size() > 2
|
||
&& str[0] == 'g'
|
||
&& str[1] == '_';
|
||
}
|
||
|
||
dl::ScriptExprTokenType ScriptSystem::GetTokenType(std::string_view str)
|
||
{
|
||
if (IsOperator(str))
|
||
return ScriptExprTokenType::ESET_operator;
|
||
else if (IsVariable(str))
|
||
return ScriptExprTokenType::ESET_variable;
|
||
else
|
||
return ScriptExprTokenType::ESET_literal;
|
||
}
|
||
|
||
dl::ScriptExprToken ScriptSystem::MakeExprToken(ScriptBlock& block, const ScriptToken& token)
|
||
{
|
||
ScriptExprToken ret;
|
||
if (token._type == ScriptToken::STRING)
|
||
ret._type = ScriptExprTokenType::ESET_literal;
|
||
else
|
||
ret._type = GetTokenType(token._str);
|
||
switch (ret._type)
|
||
{
|
||
case dl::ScriptExprTokenType::ESET_variable:
|
||
ret._name = token._str;
|
||
break;
|
||
case dl::ScriptExprTokenType::ESET_literal:
|
||
ret._name = token._str;
|
||
ret._value = MakeLiteral(block, token);
|
||
break;
|
||
case dl::ScriptExprTokenType::ESET_operator:
|
||
ret._name = token._str;
|
||
ret._value.FromOperator(String2Operator(token._str));
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
ScriptVar ScriptSystem::MakeLiteral(ScriptBlock& block, const ScriptToken& token)
|
||
{
|
||
ScriptVar ret;
|
||
if (token._type == ScriptToken::STRING)
|
||
ret = block._varSpaceIteral.AddString(token._str);
|
||
else
|
||
{
|
||
// 字面量类型有限 数字、字符串、表
|
||
StringType type = String::GetType(token._str);
|
||
switch (type)
|
||
{
|
||
case StringType::NORMAL:
|
||
script_print(ScriptLogType::E_compile, fmt::format("错误的字面量:{}", token._str));
|
||
break;
|
||
case StringType::INTGER:
|
||
ret.FromInt(std::stoi(token._str));
|
||
break;
|
||
case StringType::FLOAT:
|
||
ret.FromFloat(std::stof(token._str));
|
||
break;
|
||
default:
|
||
assert(0);
|
||
}
|
||
// 赋值后再设置
|
||
ret._set_space_index(SCRIPT_INDEX_VS_ITERAL);
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
ScriptOperator ScriptSystem::String2Operator(std::string_view str)
|
||
{
|
||
if (str == "=")
|
||
return ScriptOperator::E_assign;
|
||
if (str == "+")
|
||
return ScriptOperator::E_add;
|
||
if (str == "-")
|
||
return ScriptOperator::E_sub;
|
||
if (str == "*")
|
||
return ScriptOperator::E_mul;
|
||
if (str == "/")
|
||
return ScriptOperator::E_div;
|
||
if (str == ",")
|
||
return ScriptOperator::E_comma;
|
||
if (str == "==")
|
||
return ScriptOperator::E_equal;
|
||
return ScriptOperator::E_error;
|
||
}
|
||
|
||
dl::ScriptVar ScriptSystem::OpArithmetic(ScriptVM& vm, ScriptOperator op, const ScriptVar& v0, const ScriptVar& v1)
|
||
{
|
||
ScriptVar ret;
|
||
if (v0.GetType() == ScriptVarType::E_int)
|
||
{
|
||
ScriptInt vv0 = v0.ToInt();
|
||
if (v1.GetType() == ScriptVarType::E_int)
|
||
{
|
||
ScriptInt vv1 = v1.ToInt();
|
||
ScriptInt vv_ret;
|
||
switch (op)
|
||
{
|
||
case dl::ScriptOperator::E_add:
|
||
vv_ret = vv0 + vv1;
|
||
break;
|
||
case dl::ScriptOperator::E_sub:
|
||
vv_ret = vv0 - vv1;
|
||
break;
|
||
case dl::ScriptOperator::E_mul:
|
||
vv_ret = vv0 * vv1;
|
||
break;
|
||
case dl::ScriptOperator::E_div:
|
||
vv_ret = vv0 / vv1;
|
||
break;
|
||
default:
|
||
assert(0);
|
||
break;
|
||
}
|
||
ret.FromInt(vv_ret);
|
||
}
|
||
}
|
||
else if (v0.GetType() == ScriptVarType::E_string)
|
||
{
|
||
ScriptString vv0 = vm.GetVariableString(v0);
|
||
if (v1.GetType() == ScriptVarType::E_string)
|
||
{
|
||
ScriptString vv1 = vm.GetVariableString(v1);
|
||
ScriptString vv_ret;
|
||
switch (op)
|
||
{
|
||
case dl::ScriptOperator::E_add:
|
||
vv_ret = vv0 + vv1;
|
||
break;
|
||
case dl::ScriptOperator::E_sub:
|
||
case dl::ScriptOperator::E_mul:
|
||
case dl::ScriptOperator::E_div:
|
||
break;
|
||
default:
|
||
assert(0);
|
||
break;
|
||
}
|
||
ret = vm._varSpaceTemp.AddString(vv_ret);
|
||
}
|
||
}
|
||
if (ret.GetType() == ScriptVarType::E_null)
|
||
{
|
||
script_print(ScriptLogType::E_err,
|
||
fmt::format("OP:算术运算失败:{} {} {}", v0.GetTypeString(), dl::to_string(op), v1.GetTypeString()));
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
void ScriptSystem::OpAssign(ScriptVM& vm, ScriptVar& v0, const ScriptVar& v1)
|
||
{
|
||
if (v0.GetType() == ScriptVarType::E_null)
|
||
{
|
||
v0 = vm.AddVariable(v0.GetSpaceIndex(), v1.GetType());
|
||
}
|
||
if (v0.GetType() == ScriptVarType::E_int)
|
||
{
|
||
if (v1.GetType() == ScriptVarType::E_int)
|
||
{
|
||
ScriptInt vv1 = v1.ToInt();
|
||
v0.FromInt(vv1);
|
||
return;
|
||
}
|
||
else if (v1.GetType() == ScriptVarType::E_float)
|
||
{
|
||
ScriptFloat vv1 = v1.ToFloat();
|
||
v0.FromInt((ScriptInt)vv1);
|
||
return;
|
||
}
|
||
}
|
||
else if (v0.GetType() == ScriptVarType::E_float)
|
||
{
|
||
if (v1.GetType() == ScriptVarType::E_int)
|
||
{
|
||
ScriptInt vv1 = v1.ToInt();
|
||
v0.FromFloat((ScriptFloat)vv1);
|
||
return;
|
||
}
|
||
else if (v1.GetType() == ScriptVarType::E_float)
|
||
{
|
||
ScriptFloat vv1 = v1.ToFloat();
|
||
v0.FromFloat(vv1);
|
||
return;
|
||
}
|
||
}
|
||
else if (v0.GetType() == ScriptVarType::E_string)
|
||
{
|
||
if (v1.GetType() == ScriptVarType::E_string)
|
||
{
|
||
const ScriptString& vv1 = vm.GetVariableString(v1);
|
||
vm.SetVariableString(v0, vv1);
|
||
return;
|
||
}
|
||
}
|
||
else if (v0.GetType() == ScriptVarType::E_vector)
|
||
{
|
||
if (v1.GetType() == ScriptVarType::E_vector)
|
||
{
|
||
const ScriptVector& vv1 = vm.GetVariableVector(v1);
|
||
vm.SetVariableVector(v0, vv1);
|
||
return;
|
||
}
|
||
}
|
||
script_print(ScriptLogType::E_err,
|
||
fmt::format("OP:赋值失败:{} = {}", v0.GetTypeString(), v1.GetTypeString()));
|
||
}
|
||
|
||
int ex_script_print(ScriptVM& vm, ScriptParam param)
|
||
{
|
||
script_print(ScriptLogType::E_print, vm.Variable2String(param[0]));
|
||
return 0;
|
||
}
|
||
int ex_script_assert(ScriptVM& vm, ScriptParam param)
|
||
{
|
||
if (!vm.Variable2Bool(param[0]))
|
||
vm.Throw();
|
||
return 0;
|
||
}
|
||
|
||
ScriptSystem::ScriptSystem():
|
||
_varSpaceGlobal{0, 0}
|
||
{
|
||
_allKeyword["function"] = 0;
|
||
_allKeyword["param"] = 0;
|
||
_allKeyword["typeof"] = 0;
|
||
_allKeyword["is_null"] = 0;
|
||
_allKeyword["operator"] = 0;
|
||
_allKeyword["return"] = 0;
|
||
_allKeyword["new"] = 0;
|
||
_allKeyword["if"] = 0;
|
||
_allKeyword["elseif"] = 0;
|
||
_allKeyword["else"] = 0;
|
||
_allKeyword["while"] = 0;
|
||
_allKeyword["for"] = 0;
|
||
_allKeyword["goto"] = 0;
|
||
|
||
_allOperator["="] = 10;
|
||
_allOperator[","] = 11;
|
||
_allOperator["=="] = 15;
|
||
_allOperator["+"] = 20;
|
||
_allOperator["-"] = 20;
|
||
_allOperator["*"] = 21;
|
||
_allOperator["/"] = 21;
|
||
_allOperator["("] = 0;
|
||
_allOperator[")"] = 0;
|
||
|
||
RegisterFunction("print", ex_script_print);
|
||
RegisterFunction("assert", ex_script_assert);
|
||
}
|
||
|
||
void ScriptVM::Execute(const ScriptBlock& block)
|
||
{
|
||
script_print(ScriptLogType::E_info, fmt::format("开始执行代码块:\n{}", block.GetSource()));
|
||
|
||
// 记录代码块
|
||
_block = █
|
||
|
||
size_t cur = 0;
|
||
while (cur < block._allSeg.size())
|
||
{
|
||
const ScriptSegment& seg = block._allSeg[cur];
|
||
|
||
// 出栈变量空间,直到层级小于自己
|
||
while (true)
|
||
{
|
||
if(_allVarSpace.size() == 1)
|
||
break;
|
||
ScriptVarSpace* vs_top = _allVarSpace.back();
|
||
if(vs_top->GetLevel() <= seg._level)
|
||
break;
|
||
_delete_var_space(vs_top);
|
||
delete vs_top;
|
||
_allVarSpace.pop_back();
|
||
}
|
||
|
||
ScriptVarSpace* var_space = new ScriptVarSpace(
|
||
(ScriptIndexVS)_allVarSpace.size(), seg._level);
|
||
_allVarSpace.push_back(var_space);
|
||
|
||
size_t cur_expr = 0;
|
||
while (cur_expr < seg._allExpr.size())
|
||
{
|
||
const ScriptExpr& expr = seg._allExpr[cur_expr];
|
||
switch (seg._type)
|
||
{
|
||
case ScriptSegmentType::E_expr:
|
||
_exec_expr(expr);
|
||
break;
|
||
case ScriptSegmentType::E_if:
|
||
break;
|
||
case ScriptSegmentType::E_ifelse:
|
||
break;
|
||
case ScriptSegmentType::E_else:
|
||
break;
|
||
case ScriptSegmentType::E_while:
|
||
break;
|
||
case ScriptSegmentType::E_for:
|
||
break;
|
||
default:
|
||
script_print(ScriptLogType::E_damage,
|
||
fmt::format("未知表达式类型:{}行,{}", expr._lineNumber + 1, (int)seg._type));
|
||
return;
|
||
}
|
||
if (_bThrow)
|
||
{
|
||
script_print(ScriptLogType::E_err,
|
||
fmt::format("line{}: {}", expr._lineNumber + 1, expr._strRaw));
|
||
break;
|
||
}
|
||
++cur_expr;
|
||
_clear_expr();
|
||
}
|
||
|
||
++cur;
|
||
if (_bThrow)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
assert(_allVarSpace.size() > 1);
|
||
for (size_t i = 1; i < _allVarSpace.size(); ++i)
|
||
{
|
||
delete _allVarSpace[i];
|
||
}
|
||
|
||
_block = nullptr;
|
||
if(_bThrow)
|
||
script_print(ScriptLogType::E_info, fmt::format("执行代码块出错:\n{}", block.GetSource()));
|
||
else
|
||
script_print(ScriptLogType::E_info, fmt::format("执行代码块完成", ""));
|
||
}
|
||
|
||
ScriptVM::ScriptVM() :
|
||
_bThrow{ false },
|
||
_varSpaceGlobal{ 0 , 0 },
|
||
_varSpaceTemp{ SCRIPT_INDEX_VS_TEMP , 0 }
|
||
{
|
||
g_script->CopyVariableToVm(*this);
|
||
_allVarSpace.push_back(&_varSpaceGlobal);
|
||
_param.resize(16);
|
||
}
|
||
|
||
void ScriptVM::_exec_expr(const ScriptExpr& expr)
|
||
{
|
||
_expr = &expr;
|
||
_stackVar.clear();
|
||
const std::vector<ScriptExprToken>& vec_token = _expr->_allToken;
|
||
|
||
for (size_t i = 0; i < vec_token.size(); ++i)
|
||
{
|
||
const ScriptExprToken& token = vec_token[i];
|
||
if (token._type == ScriptExprTokenType::ESET_operator)
|
||
{
|
||
ScriptOperator op = token._value.ToOperator();
|
||
switch (op)
|
||
{
|
||
case ScriptOperator::E_assign:
|
||
_op_assign();
|
||
break;
|
||
case ScriptOperator::E_add:
|
||
case ScriptOperator::E_sub:
|
||
case ScriptOperator::E_mul:
|
||
case ScriptOperator::E_div:
|
||
_op_arithmetic(op);
|
||
break;
|
||
case ScriptOperator::E_comma:
|
||
_op_comma();
|
||
break;
|
||
case ScriptOperator::E_equal:
|
||
_op_equal();
|
||
break;
|
||
default:
|
||
script_print(ScriptLogType::E_damage, fmt::format("未知操作符:{}", token._name));
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
_stackVar.push_back((ScriptIndex)i);
|
||
}
|
||
}
|
||
// 剩余栈处理
|
||
if (_stackVar.size() == 0)
|
||
int temp = 0;
|
||
if (_stackVar.size() == 1)
|
||
int temp = 1;
|
||
else if (_stackVar.size() == 2)
|
||
{
|
||
ScriptVar& var0 = _get_stack_var(0);
|
||
ScriptVar& var1 = _get_stack_var(1);
|
||
_stack_pop(2);
|
||
ScriptFunction func = GetVariableFunction(var0);
|
||
_param[0] = var1;
|
||
func(*this, _param);
|
||
}
|
||
else
|
||
{
|
||
script_print(ScriptLogType::E_damage,
|
||
fmt::format("剩余栈错误:{}", dl::to_string(_stackVar)));
|
||
}
|
||
int a = 3;
|
||
}
|
||
|
||
void ScriptVM::_clear_expr()
|
||
{
|
||
_stackVar.clear();
|
||
}
|
||
|
||
const ScriptExprToken& ScriptVM::_get_stack_expr_token(size_t i)
|
||
{
|
||
if(i >= _stackVar.size())
|
||
{
|
||
script_print(ScriptLogType::E_damage, "stack越界!");
|
||
return _return;
|
||
}
|
||
if (_stackVar[i] == SCRIPT_INDEX_RETURN)
|
||
return _return;
|
||
else
|
||
return _expr->_allToken[_stackVar[i]];
|
||
}
|
||
|
||
const ScriptExprToken& ScriptVM::_get_stack_expr_token_top(size_t i)
|
||
{
|
||
i = _stackVar.size() - i - 1;
|
||
return _get_stack_expr_token(i);
|
||
}
|
||
|
||
dl::ScriptVar& ScriptVM::_get_stack_var(size_t i)
|
||
{
|
||
return _get_variable(_get_stack_expr_token(i));
|
||
}
|
||
|
||
|
||
dl::ScriptVar& ScriptVM::_get_stack_var_top(size_t i)
|
||
{
|
||
i = _stackVar.size() - i - 1;
|
||
return _get_stack_var(i);
|
||
}
|
||
|
||
void ScriptVM::_stack_pop(size_t i)
|
||
{
|
||
i = _stackVar.size() - i;
|
||
if (i > _stackVar.size())
|
||
{
|
||
script_print(ScriptLogType::E_damage, "stack数量不足!");
|
||
return;
|
||
}
|
||
_stackVar.resize(i);
|
||
}
|
||
|
||
void ScriptVM::_op_arithmetic(ScriptOperator op)
|
||
{
|
||
ScriptVar& var0 = _get_stack_var_top(1);
|
||
ScriptVar& var1 = _get_stack_var_top(0);
|
||
_stack_pop(2);
|
||
|
||
_stackVar.push_back(SCRIPT_INDEX_RETURN);
|
||
_return._type = ScriptExprTokenType::ESET_variable;
|
||
_return._value = g_script->OpArithmetic(*this, op, var0, var1);
|
||
_return._name = {};
|
||
}
|
||
|
||
void ScriptVM::_op_assign()
|
||
{
|
||
const ScriptExprToken& t0 = _get_stack_expr_token_top(1);
|
||
ScriptVar& var0 = _get_stack_var_top(1);
|
||
ScriptVar& var1 = _get_stack_var_top(0);
|
||
_stack_pop(2);
|
||
|
||
g_script->OpAssign(*this, var0, var1);
|
||
|
||
_stackVar.push_back(SCRIPT_INDEX_RETURN);
|
||
_return._type = ScriptExprTokenType::ESET_variable;
|
||
_return._value = var0;
|
||
_return._name = t0._name;
|
||
}
|
||
|
||
void ScriptVM::_op_comma()
|
||
{
|
||
ScriptVar& var0 = _get_stack_var_top(1);
|
||
ScriptVar& var1 = _get_stack_var_top(0);
|
||
_stack_pop(2);
|
||
|
||
if (var0.GetType() == ScriptVarType::E_vector)
|
||
{// 0为容器则加到容器
|
||
ScriptVector& vec = GetVariableVector(var0);
|
||
vec.push_back(var1);
|
||
_return._value = var0;
|
||
}
|
||
else
|
||
{// 新建临时容器
|
||
ScriptVar var = _varSpaceTemp.AddVector();
|
||
ScriptVector& vec = GetVariableVector(var);
|
||
vec.push_back(var0);
|
||
vec.push_back(var1);
|
||
_return._value = var;
|
||
}
|
||
_stackVar.push_back(SCRIPT_INDEX_RETURN);
|
||
_return._type = ScriptExprTokenType::ESET_variable;
|
||
_return._name = {};
|
||
}
|
||
|
||
void ScriptVM::_op_equal()
|
||
{
|
||
ScriptVar& var0 = _get_stack_var_top(1);
|
||
ScriptVar& var1 = _get_stack_var_top(0);
|
||
_stack_pop(2);
|
||
|
||
bool equal = true;
|
||
do
|
||
{
|
||
if (var0.GetType() != var1.GetType())
|
||
{
|
||
equal = false;
|
||
break;
|
||
}
|
||
if (IsBaseType(var0.GetType()))
|
||
{
|
||
equal = var0.GetValue() == var1.GetValue();
|
||
break;
|
||
}
|
||
if (var0.GetType() == ScriptVarType::E_string)
|
||
{
|
||
equal = GetVariableString(var0) == GetVariableString(var1);
|
||
break;
|
||
}
|
||
// todo
|
||
equal = false;
|
||
} while (0);
|
||
|
||
_stackVar.push_back(SCRIPT_INDEX_RETURN);
|
||
_return._type = ScriptExprTokenType::ESET_variable;
|
||
_return._value.FromInt(equal ? 1 : 0);
|
||
_return._name = {};
|
||
}
|
||
|
||
ScriptVar& ScriptVM::_get_variable(const ScriptExprToken& t)
|
||
{
|
||
if (t._type == ScriptExprTokenType::ESET_literal)
|
||
{// 字面量
|
||
return (ScriptVar&)t._value;
|
||
}
|
||
// 空名变量视为返回值
|
||
if (t._name.empty())
|
||
{
|
||
return _return._value;
|
||
}
|
||
auto iter = _allVar.find(t._name);
|
||
if (iter == _allVar.end())
|
||
{
|
||
// 新建
|
||
ScriptVar& var = _allVar[t._name];
|
||
if (g_script->IsGlobalVariable(t._name))
|
||
var._set_space_index(0);
|
||
else
|
||
{
|
||
assert(_allVarSpace.size() > 1);
|
||
var._set_space_index((ScriptIndexVS)_allVarSpace.size() - 1);
|
||
}
|
||
return var;
|
||
}
|
||
return _allVar[t._name];
|
||
}
|
||
|
||
void ScriptVM::_delete_var_space(ScriptVarSpace* vs)
|
||
{
|
||
for (auto iter = _allVar.begin(); iter != _allVar.end();)
|
||
{
|
||
if (iter->second.GetSpaceIndex() == vs->GetIndex())
|
||
iter = _allVar.erase(iter);
|
||
else
|
||
++iter;
|
||
}
|
||
int x = 3;
|
||
}
|
||
|
||
dl::ScriptVarSpace* ScriptVM::GetVariableSpaceRef(ScriptIndexVS index)
|
||
{
|
||
if (index == SCRIPT_INDEX_VS_TEMP)
|
||
return &_varSpaceTemp;
|
||
if (index >= _allVarSpace.size())
|
||
{
|
||
script_print(ScriptLogType::E_damage, "变量空间下标错误!");
|
||
return {};
|
||
}
|
||
return _allVarSpace[index];
|
||
}
|
||
|
||
const dl::ScriptVarSpace* ScriptVM::GetVariableSpace(ScriptIndexVS index)
|
||
{
|
||
if (index == SCRIPT_INDEX_VS_ITERAL)
|
||
return &_block->_varSpaceIteral;
|
||
if (index == SCRIPT_INDEX_VS_TEMP)
|
||
return &_varSpaceTemp;
|
||
if (index >= _allVarSpace.size())
|
||
{
|
||
script_print(ScriptLogType::E_damage, "变量空间下标错误!");
|
||
return {};
|
||
}
|
||
return _allVarSpace[index];
|
||
}
|
||
|
||
dl::ScriptVar ScriptVM::AddVariable(ScriptIndexVS index, ScriptVarType type)
|
||
{
|
||
ScriptVar v;
|
||
// nil -> 构造实际的变量
|
||
if (type == ScriptVarType::E_string)
|
||
{
|
||
v = GetVariableSpaceRef(index)->AddString({});
|
||
}
|
||
else if (type == ScriptVarType::E_vector)
|
||
{
|
||
v = GetVariableSpaceRef(index)->AddVector();
|
||
}
|
||
else
|
||
{
|
||
// nil -> 基础类型默认值0
|
||
v.SetType(type);
|
||
v.SetValue(0);
|
||
}
|
||
return v;
|
||
}
|
||
|
||
dl::ScriptFunction ScriptVM::GetVariableFunction(ScriptVar var)
|
||
{
|
||
auto space = GetVariableSpace(var);
|
||
return space->GetFunction(var.GetIndex());
|
||
}
|
||
|
||
dl::ScriptString ScriptVM::GetVariableString(ScriptVar var)
|
||
{
|
||
auto space = GetVariableSpace(var);
|
||
return space->GetString(var.GetIndex());
|
||
}
|
||
|
||
dl::ScriptVector& ScriptVM::GetVariableVector(ScriptVar var)
|
||
{
|
||
auto space = GetVariableSpaceRef(var);
|
||
return space->GetVetorRef(var.GetIndex());
|
||
}
|
||
|
||
void ScriptVM::SetVariableString(ScriptVar var, ScriptString str)
|
||
{
|
||
auto space = GetVariableSpaceRef(var);
|
||
return space->SetString(var.GetIndex(), str);
|
||
}
|
||
|
||
void ScriptVM::SetVariableVector(ScriptVar var, const ScriptVector& vec)
|
||
{
|
||
auto space = GetVariableSpaceRef(var);
|
||
return space->SetVector(var.GetIndex(), vec);
|
||
}
|
||
|
||
std::string ScriptVM::Variable2String(const ScriptVar& var)
|
||
{
|
||
switch (var.GetType())
|
||
{
|
||
case ScriptVarType::E_null:
|
||
return "null";
|
||
case ScriptVarType::E_int:
|
||
return std::to_string(var.ToInt());
|
||
case ScriptVarType::E_float:
|
||
return std::to_string(var.ToFloat());
|
||
case ScriptVarType::E_string:
|
||
return GetVariableString(var);
|
||
case ScriptVarType::E_class:
|
||
case ScriptVarType::E_vector:
|
||
{
|
||
std::string ret;
|
||
const ScriptVector& vec = GetVariableVector(var);
|
||
ret += '[';
|
||
for (size_t i = 0; i < vec.size(); ++i)
|
||
{
|
||
if (vec[i] == var)
|
||
{// todo 深层循环引用
|
||
ret += "self";
|
||
}
|
||
else
|
||
{
|
||
ret += Variable2String(vec[i]);
|
||
}
|
||
if (i + 1 < vec.size())
|
||
ret += ", ";
|
||
}
|
||
ret += ']';
|
||
return ret;
|
||
}
|
||
case ScriptVarType::E_table:
|
||
case ScriptVarType::E_function:
|
||
case ScriptVarType::E_userdata:
|
||
case ScriptVarType::E_operator:
|
||
case ScriptVarType::E_max:
|
||
default:
|
||
break;
|
||
}
|
||
assert(0);
|
||
return "error_variable";
|
||
}
|
||
|
||
bool ScriptVM::Variable2Bool(const ScriptVar& var)
|
||
{
|
||
switch (var.GetType())
|
||
{
|
||
case ScriptVarType::E_null:
|
||
return false;
|
||
case ScriptVarType::E_int:
|
||
return var.ToInt();
|
||
case ScriptVarType::E_float:
|
||
return var.ToFloat();
|
||
case ScriptVarType::E_string:
|
||
return !GetVariableString(var).empty();
|
||
case ScriptVarType::E_class:
|
||
case ScriptVarType::E_vector:
|
||
case ScriptVarType::E_table:
|
||
case ScriptVarType::E_function:
|
||
case ScriptVarType::E_userdata:
|
||
case ScriptVarType::E_operator:
|
||
case ScriptVarType::E_max:
|
||
default:
|
||
break;
|
||
}
|
||
script_print(ScriptLogType::E_err,
|
||
fmt::format("变量类型不支持转换bool:{}", var.GetTypeString()));
|
||
return false;
|
||
}
|
||
|
||
std::string_view ScriptVar::GetTypeString() const
|
||
{
|
||
switch (_type)
|
||
{
|
||
case dl::ScriptVarType::E_null:
|
||
return "null";
|
||
case dl::ScriptVarType::E_int:
|
||
return "int";
|
||
case dl::ScriptVarType::E_float:
|
||
return "float";
|
||
case dl::ScriptVarType::E_string:
|
||
return "string";
|
||
case dl::ScriptVarType::E_class:
|
||
return "class";
|
||
case dl::ScriptVarType::E_vector:
|
||
return "vector";
|
||
case dl::ScriptVarType::E_table:
|
||
return "table";
|
||
case dl::ScriptVarType::E_function:
|
||
return "function";
|
||
case dl::ScriptVarType::E_userdata:
|
||
return "userdata";
|
||
case dl::ScriptVarType::E_operator:
|
||
return "operator";
|
||
case dl::ScriptVarType::E_max:
|
||
default:
|
||
script_print(ScriptLogType::E_damage,
|
||
fmt::format("错误的变量类型:{}", (int)_type));
|
||
return "error_var_type";
|
||
}
|
||
}
|
||
|
||
std::string_view to_string(ScriptOperator op)
|
||
{
|
||
switch (op)
|
||
{
|
||
case dl::ScriptOperator::E_assign:
|
||
return "=";
|
||
case dl::ScriptOperator::E_add:
|
||
return "+";
|
||
case dl::ScriptOperator::E_sub:
|
||
return "-";
|
||
case dl::ScriptOperator::E_mul:
|
||
return "*";
|
||
case dl::ScriptOperator::E_div:
|
||
return "/";
|
||
case dl::ScriptOperator::E_comma:
|
||
return ",";
|
||
case dl::ScriptOperator::E_equal:
|
||
return "==";
|
||
default:
|
||
return "error_op";
|
||
}
|
||
}
|
||
|
||
|
||
std::string_view to_string(ScriptToken::Type type)
|
||
{
|
||
switch (type)
|
||
{
|
||
case dl::ScriptToken::NORMAL:
|
||
return "普通";
|
||
case dl::ScriptToken::STRING:
|
||
return "字符串";
|
||
//case dl::ScriptToken::LBRACE:
|
||
// return "左括号";
|
||
//case dl::ScriptToken::RBRACE:
|
||
// return "右括号";
|
||
default:
|
||
break;
|
||
}
|
||
return "error_token";
|
||
}
|
||
|
||
|
||
std::string ScriptBlock::GetSource(bool line_number /*= true*/,
|
||
bool header /*= true*/, bool footer /*= true*/) const
|
||
{
|
||
std::string ret;
|
||
if (header)
|
||
ret += "--------------------------------\n";
|
||
for (size_t i = 0; i < _vecSource.size(); ++i)
|
||
{
|
||
if (line_number)
|
||
ret += fmt::format("{: >6}.|", i + 1);
|
||
ret += _vecSource[i];
|
||
ret += "\n";
|
||
}
|
||
if (footer)
|
||
ret += "--------------------------------";
|
||
else
|
||
ret.pop_back();
|
||
return ret;
|
||
}
|
||
|
||
}
|