#include "eval.h" #include "json.h" #include #include #include #include TLuaEval::TLuaEval() : FunctionNameCounter_(0) { LuaState_.BootStrap(); } void TLuaEval::SetVariable(TZtStringBuf name, const NJson::TJsonValue& value) { TGuard guard(LuaMutex_); NLua::PushJsonValue(&LuaState_, value); LuaState_.set_global(name.c_str()); } void TLuaEval::RunExpressionLocked(const TGuard&, const TExpression& expr) { LuaState_.push_global(expr.Name.c_str()); LuaState_.call(0, 1); } TString TLuaEval::EvalCompiled(const TExpression& expr) { TGuard guard(LuaMutex_); RunExpressionLocked(guard, expr); return LuaState_.pop_value(); } void TLuaEval::EvalCompiledRaw(const TExpression& expr) { TGuard guard(LuaMutex_); RunExpressionLocked(guard, expr); } bool TLuaEval::EvalCompiledCondition(const TExpression& expr) { TGuard guard(LuaMutex_); RunExpressionLocked(guard, expr); return LuaState_.pop_bool_strict(); } TString TLuaEval::EvalRaw(TStringBuf code) { TMemoryInput bodyIn(code.data(), code.size()); LuaState_.Load(&bodyIn, "main"); LuaState_.call(0, 1); return LuaState_.pop_value(); } void TLuaEval::ParseChunk(TStringBuf code) { TMemoryInput in(code.data(), code.size()); LuaState_.Load(&in, "chunk_" + GenerateName()); LuaState_.call(0, 0); } TString TLuaEval::EvalExpression(TStringBuf expression) { const auto expr = Compile(expression); try { return EvalCompiled(expr); } catch (const yexception& e) { throw yexception(e) << '\n' << expression; } } TLuaEval::TExpression TLuaEval::Compile(TStringBuf expression) { TGuard guard(LuaMutex_); TString name = GenerateName(); TString body = "function "; body += name; body += "()\n\treturn ("; body += expression; body += ")\nend\n"; try { TMemoryInput bodyIn(body.c_str(), body.size()); LuaState_.Load(&bodyIn, "chunk_" + name); LuaState_.call(0, 0); } catch (const yexception& e) { ythrow yexception(e) << "\n" << body; } return {name}; } TLuaEval::TExpression TLuaEval::CompileFunction(TStringBuf expression) { TString name = GenerateName(); TStringBuilder body; body << "function " << name << "()" << Endl << expression << Endl << "end"; return CompileRaw(TStringBuf(body.data(), body.size()), name); } TLuaEval::TExpression TLuaEval::CompileRaw(TStringBuf body, const TString& name) { TGuard guard(LuaMutex_); try { TMemoryInput bodyIn(body.data(), body.size()); LuaState_.Load(&bodyIn, "chunk_" + name); LuaState_.call(0, 0); } catch (const yexception& e) { ythrow yexception(e) << "\n" << body; } return { name }; } TString TLuaEval::DumpStack() { TString result; { TStringOutput so(result); LuaState_.DumpStack(&so); } return result; } TString TLuaEval::GenerateName() { TGuard guard(LuaMutex_); return "dummy_" + ToString(FunctionNameCounter_++); } template static inline T FindEnd(T b, T e) { size_t cnt = 0; while (b < e) { switch (*b) { case '{': ++cnt; break; case '}': if (cnt == 0) { return b; } --cnt; break; } ++b; } return b; } TString TLuaEval::PreprocessOne(TStringBuf line) { const size_t pos = line.find("${"); if (pos == TStringBuf::npos) { return EvalExpression(line); } const char* rpos = FindEnd(line.data() + pos + 2, line.end()); if (rpos == line.end()) { ythrow yexception() << TStringBuf("can not parse ") << line; } const TStringBuf before = line.SubStr(0, pos); const TStringBuf after = TStringBuf(rpos + 1, line.end()); const TStringBuf code = TStringBuf(line.data() + pos + 2, rpos); TString res; if (code.find("${") == TStringBuf::npos) { res = EvalExpression(code); } else { res = EvalExpression(Preprocess(code)); } return ToString(before) + res + ToString(after); } bool TLuaEval::CheckEmptyStack() { for (int i = 1; i <= LuaState_.on_stack(); ++i) { if (!LuaState_.is_nil(-1 * i)) { return false; } } return true; } TString TLuaEval::Preprocess(TStringBuf line) { TString res = ToString(line); while (res.find("${") != TString::npos) { res = PreprocessOne(res); } return res; }