initial commit

This commit is contained in:
2026-08-03 16:09:07 -04:00
commit 95fa512047
109 changed files with 35023 additions and 0 deletions
+115
View File
@@ -0,0 +1,115 @@
// Loading Lua off the SD card. Lua's stock loaders go through stdio, which cannot see the
// mount, so every path into the filesystem goes through FsProvider instead.
#include <lua/runtime.h>
extern "C" {
#include "lauxlib.h"
#include "lua.h"
}
namespace esp32lua {
namespace {
struct ChunkReader {
FileReader* file;
char buffer[512];
};
const char* readChunk(lua_State*, void* context, size_t* size) {
ChunkReader* reader = static_cast<ChunkReader*>(context);
const int32_t read = reader->file->read(reader->buffer, sizeof(reader->buffer));
*size = read > 0 ? static_cast<size_t>(read) : 0;
return read > 0 ? reader->buffer : nullptr;
}
// Loads a path onto the stack as a chunk, or pushes nothing and returns a Lua status.
int load(lua_State* state, FsProvider& fs, const std::string& path) {
ChunkReader reader;
reader.file = fs.openRead(path);
if (!reader.file) {
lua_pushfstring(state, "cannot open %s", path.c_str());
return LUA_ERRFILE;
}
const std::string chunkname = "@" + path;
const int status = lua_load(state, readChunk, &reader, chunkname.c_str(), "t");
delete reader.file;
return status;
}
} // namespace
bool Runtime::loadScript(const std::string& path) {
return load(state_, *providers_.fs, path) == LUA_OK;
}
// Resolves a module name against package.path, reporting every path tried the way the stock
// searcher does.
int Runtime::searchModule(lua_State* state) {
Runtime* runtime = Runtime::from(state);
std::string name = luaL_checkstring(state, 1);
for (size_t at = 0; at < name.size(); at++) {
if (name[at] == '.') name[at] = '/';
}
lua_getglobal(state, "package");
lua_getfield(state, -1, "path");
const std::string templates = luaL_optstring(state, -1, "");
lua_pop(state, 2);
std::string tried;
size_t start = 0;
while (start <= templates.size()) {
const size_t end = templates.find(';', start);
std::string candidate = templates.substr(start, end == std::string::npos ? std::string::npos : end - start);
start = end == std::string::npos ? templates.size() + 1 : end + 1;
if (candidate.empty()) continue;
const size_t mark = candidate.find('?');
if (mark != std::string::npos) candidate.replace(mark, 1, name);
if (!runtime->providers_.fs->exists(candidate)) {
tried += "\n\tno file '" + candidate + "'";
continue;
}
if (load(state, *runtime->providers_.fs, candidate) != LUA_OK) {
return luaL_error(state, "error loading module '%s' from '%s':\n\t%s", luaL_checkstring(state, 1),
candidate.c_str(), lua_tostring(state, -1));
}
lua_pushstring(state, candidate.c_str());
return 2;
}
lua_pushstring(state, tried.c_str());
return 1;
}
int Runtime::loadFile(lua_State* state) {
Runtime* runtime = Runtime::from(state);
if (load(state, *runtime->providers_.fs, luaL_checkstring(state, 1)) == LUA_OK) return 1;
lua_pushnil(state);
lua_insert(state, -2);
return 2;
}
void Runtime::installLoader(const std::string& appDir) {
lua_getglobal(state_, "package");
const std::string path = appDir + "/?.lua;" + paths_.lib + "/?.lua";
lua_pushlstring(state_, path.data(), path.size());
lua_setfield(state_, -2, "path");
// Keep the preload searcher, drop the C loaders: they can only report misleading errors
// about shared objects that were never there.
lua_getfield(state_, -1, "searchers");
lua_pushcfunction(state_, searchModule);
lua_rawseti(state_, -2, 2);
for (int at = 3; at <= 4; at++) {
lua_pushnil(state_);
lua_rawseti(state_, -2, at);
}
lua_pop(state_, 2);
lua_pushcfunction(state_, loadFile);
lua_setglobal(state_, "loadfile");
}
} // namespace esp32lua