aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/lua/eval.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'library/cpp/lua/eval.cpp')
-rw-r--r--library/cpp/lua/eval.cpp178
1 files changed, 178 insertions, 0 deletions
diff --git a/library/cpp/lua/eval.cpp b/library/cpp/lua/eval.cpp
new file mode 100644
index 0000000000..5c78d0ad3f
--- /dev/null
+++ b/library/cpp/lua/eval.cpp
@@ -0,0 +1,178 @@
+#include "eval.h"
+#include "json.h"
+#include <util/string/cast.h>
+#include <util/system/guard.h>
+#include <util/stream/mem.h>
+#include <util/string/builder.h>
+
+TLuaEval::TLuaEval()
+ : FunctionNameCounter_(0)
+{
+ LuaState_.BootStrap();
+}
+
+void TLuaEval::SetVariable(TZtStringBuf name, const NJson::TJsonValue& value) {
+ TGuard<TMutex> guard(LuaMutex_);
+
+ NLua::PushJsonValue(&LuaState_, value);
+ LuaState_.set_global(name.c_str());
+}
+
+void TLuaEval::RunExpressionLocked(const TGuard<TMutex>&, const TExpression& expr) {
+ LuaState_.push_global(expr.Name.c_str());
+ LuaState_.call(0, 1);
+}
+
+TString TLuaEval::EvalCompiled(const TExpression& expr) {
+ TGuard<TMutex> guard(LuaMutex_);
+ RunExpressionLocked(guard, expr);
+ return LuaState_.pop_value();
+}
+
+void TLuaEval::EvalCompiledRaw(const TExpression& expr) {
+ TGuard<TMutex> guard(LuaMutex_);
+ RunExpressionLocked(guard, expr);
+}
+
+bool TLuaEval::EvalCompiledCondition(const TExpression& expr) {
+ TGuard<TMutex> 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<TMutex> 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<TMutex> 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::GenerateName() {
+ TGuard<TMutex> guard(LuaMutex_);
+ return "dummy_" + ToString(FunctionNameCounter_++);
+}
+
+template <class T>
+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);
+}
+
+TString TLuaEval::Preprocess(TStringBuf line) {
+ TString res = ToString(line);
+
+ while (res.find("${") != TString::npos) {
+ res = PreprocessOne(res);
+ }
+
+ return res;
+}