Add VALIDATE_LUA as a function in lpxserver and lpxclient

This commit is contained in:
2025-12-03 19:55:53 -05:00
parent 27b5ce7ef4
commit a242244f9c
9 changed files with 86 additions and 27 deletions

View File

@@ -32,6 +32,7 @@ enum class AccessKind {
INVOKE_LUA_SOURCE,
PROBE_LUA_CALL,
CONNECT_TO_SERVER,
VALIDATE_LUA,
};
class DrivenEngine;

View File

@@ -20,14 +20,21 @@ public:
LuaConsole console_;
PrintChanneler print_channeler_;
eng::vector<Invocation> delayed_invocations_;
lua_State *lua_syntax_checker_;
public:
LpxClient() {
lua_syntax_checker_ = LuaCoreStack::newstate(eng::l_alloc);
set_console_prompt(console_.get_prompt());
set_initial_state_standalone();
}
~LpxClient() {
lua_close(lua_syntax_checker_);
}
void set_initial_state_connect(const eng::string &hostspec) {
// Create the world model.
world_.reset(new World(WORLD_TYPE_PREDICTIVE));
@@ -256,6 +263,13 @@ public:
set_initial_state_connect(util::ss("nocert:", datapk, ":8085"));
break;
}
case AccessKind::VALIDATE_LUA: {
LuaVar closure;
LuaDefStack LS(lua_syntax_checker_, closure);
eng::string errmsg = LS.load(closure, datapk, "stdin");
retpk->write_bytes(errmsg);
break;
}
default: {
util::dprint("Invalid event_access: ", int(kind));
}

View File

@@ -6,6 +6,7 @@
#include "luaconsole.hpp"
#include "util.hpp"
#include "printbuffer.hpp"
#include "luastack.hpp"
#include <memory>
@@ -33,10 +34,14 @@ public:
eng::vector<Invocation> delayed_invocations_;
int64_t admin_id_ = 0;
double next_tick_ = 0;
lua_State *lua_syntax_checker_;
public:
LpxServer()
{
// Create a little lua interpreter for syntax checking only.
lua_syntax_checker_ = LuaCoreStack::newstate(eng::l_alloc);
// Create the master world model.
master_.reset(new World(WORLD_TYPE_MASTER));
@@ -62,6 +67,10 @@ public:
rescan_lua_source(true);
}
~LpxServer() {
lua_close(lua_syntax_checker_);
}
virtual void do_syntax_error(std::string_view error) override {
stdostream() << "Syntax error: " << error << std::endl;
}
@@ -178,6 +187,13 @@ public:
master_->rollback();
break;
}
case AccessKind::VALIDATE_LUA: {
LuaVar closure;
LuaDefStack LS(lua_syntax_checker_, closure);
eng::string errmsg = LS.load(closure, datapk, "stdin");
retpk->write_bytes(errmsg);
break;
}
default: {
stdostream() << "Invalid event_access: " << int(kind) << std::endl;
}

View File

@@ -89,26 +89,23 @@ void LuaConsole::add(eng::string line) {
}
// Try to parse the lua expression
int top = lua_gettop(lua_state_);
int status = luaL_loadbuffer(lua_state_, partial.c_str(), partial.size(), "=stdin");
if (status == LUA_ERRSYNTAX)
{
const char *eof = "<eof>";
int leof = strlen(eof);
size_t lmsg;
const char *msg = lua_tolstring(lua_state_, -1, &lmsg);
const char *tp = msg + lmsg - leof;
if (strstr(msg, eof) != tp) {
LuaVar result;
LuaDefStack LS(lua_state_, result);
eng::string errmsg = LS.load(result, partial, "stdin");
if (errmsg.empty()) {
words_.push_back(lua_mode);
words_.emplace_back(sv::rtrim(partial));
clear_raw_input();
} else if (errmsg.find("<eof>") != std::string::npos) {
// We have an incomplete expression.
// Do nothing, just let the user type more stuff.
} else {
words_.push_back("syntax");
words_.push_back(msg);
words_.push_back(errmsg);
clear_raw_input();
}
} else {
words_.push_back(lua_mode);
words_.emplace_back(sv::rtrim(partial));
clear_raw_input();
}
lua_settop(lua_state_, top);
}
void CommonCommands::do_command(const StringVec &words) {

View File

@@ -323,6 +323,29 @@ bool LuaCoreStack::next(LuaSlot tab, LuaSlot key, LuaSlot value) const {
return (ret != 0);
}
eng::string LuaCoreStack::load(LuaSlot result, std::string_view code, std::string_view context)
{
eng::string fullcontext = eng::string("=") + eng::string(context);
luaL_loadbuffer(L_, code.data(), code.size(), fullcontext.c_str());
int type = lua_type(L_, -1);
if (type == LUA_TFUNCTION) {
// compiler returned a closure.
lua_replace(L_, result.index());
return "";
} else if (type == LUA_TSTRING) {
// compiler returned an error message.
size_t len;
const char *str = lua_tolstring(L_, -1, &len);
eng::string message(str, len);
lua_pop(L_, 1);
if (message.empty()) message = "unknown compiler error";
set(result, message);
return message;
} else {
assert(false && "lua compiler didn't return a closure, but didn't return an error message either");
}
}
void LuaCoreStack::getglobaltable(LuaSlot target) const {
lua_pushglobaltable(L_);
lua_replace(L_, target);

View File

@@ -792,6 +792,19 @@ public:
//
bool next(LuaSlot tab, LuaSlot key, LuaSlot val) const;
// Compile lua code.
//
// If the code contains a syntax error, then the result variable
// is set to the error message, and the error message is returned.
//
// If the code is valid, then the result variable is set to a
// closure, and an empty string is returned.
//
// If a syntax error occurs, the error message may contain the
// token <eof>. If so, the problem is an incomplete expression.
//
eng::string load(LuaSlot result, std::string_view code, std::string_view context);
// Return true if the int64 can be stored losslessly in a lua_Number.
//
// Lua numbers are actually double-precision floating point. double

View File

@@ -78,9 +78,7 @@ static void calculate_loadresult(LuaCoreStack &LS0, LuaSlot info, const eng::str
if (code == "") {
LS.rawset(info, "loadresult", "missing or empty source file");
} else {
eng::string chunk = "=" + fn;
luaL_loadbuffer(LS.state(), code.c_str(), code.size(), chunk.c_str());
lua_replace(LS.state(), loadresult.index());
LS.load(loadresult, code, fn);
LS.rawset(info, "loadresult", loadresult);
}
}

View File

@@ -377,14 +377,10 @@ eng::string World::probe_lua_expr(int64_t actor_id, std::string_view lua) {
LuaVar closure;
LuaExtStack LS(L, closure);
// create the compiled closure.
int status = luaL_loadbuffer(L, lua.data(), lua.size(), "=probe");
lua_replace(L, closure.index());
if (status != LUA_OK) {
// The closure is actually an error message. Do nothing.
// This should normally not happen: LuaConsole should filter
// out syntax errors.
eng::string errmsg = LS.load(closure, lua, "probe");
if (!errmsg.empty()) {
// This should normally not happen: the front end should
// filter expressions for syntactic lua validity.
return "";
}