Scripting: Start bringing up Lua bindings

This commit is contained in:
Vicki Pfau 2022-02-15 17:44:21 -08:00
parent 512572769e
commit ce97d86906
5 changed files with 193 additions and 0 deletions

View File

@ -57,6 +57,7 @@ if(NOT LIBMGBA_ONLY)
set(USE_LIBZIP ON CACHE BOOL "Whether or not to enable LIBZIP support")
set(USE_SQLITE3 ON CACHE BOOL "Whether or not to enable SQLite3 support")
set(USE_ELF ON CACHE BOOL "Whether or not to enable ELF support")
set(USE_LUA ON CACHE BOOL "Whether or not to enable Lua scripting support")
set(M_CORE_GBA ON CACHE BOOL "Build Game Boy Advance core")
set(M_CORE_GB ON CACHE BOOL "Build Game Boy core")
set(USE_LZMA ON CACHE BOOL "Whether or not to enable 7-Zip support")
@ -735,6 +736,17 @@ endif()
if(ENABLE_SCRIPTING)
add_subdirectory(src/script)
list(APPEND ENABLES SCRIPTING)
if(NOT USE_LUA VERSION_LESS 5.1)
find_feature(USE_LUA "Lua" ${USE_LUA})
else()
find_feature(USE_LUA "Lua")
endif()
if(USE_LUA)
list(APPEND FEATURE_DEFINES USE_LUA)
include_directories(AFTER ${LUA_INCLUDE_DIR})
list(APPEND FEATURE_DEFINES LUA_VERSION_ONLY=\"${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}\")
list(APPEND DEPENDENCY_LIB ${LUA_LIBRARY})
endif()
if(BUILD_PYTHON)
find_package(PythonLibs ${USE_PYTHON_VERSION})
@ -1226,6 +1238,14 @@ if(NOT QUIET AND NOT LIBMGBA_ONLY)
message(STATUS " ELF loading support: ${USE_ELF}")
message(STATUS " Discord Rich Presence support: ${USE_DISCORD_RPC}")
message(STATUS " OpenGL support: ${SUMMARY_GL}")
message(STATUS "Scripting support: ${ENABLE_SCRIPTING}")
if(ENABLE_SCRIPTING)
if(LUA_VERSION_STRING)
message(STATUS " Lua: ${LUA_VERSION_STRING}")
else()
message(STATUS " Lua: ${USE_LUA}")
endif()
endif()
message(STATUS "Frontends:")
message(STATUS " Qt: ${BUILD_QT}")
message(STATUS " SDL (${SDL_VERSION}): ${BUILD_SDL}")

View File

@ -0,0 +1,12 @@
/* Copyright (c) 2013-2022 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef M_SCRIPT_LUA_H
#define M_SCRIPT_LUA_H
#include <mgba/script/context.h>
extern struct mScriptEngine2* const mSCRIPT_ENGINE_LUA;
#endif

View File

@ -6,6 +6,11 @@ set(SOURCE_FILES
set(TEST_FILES
test/types.c)
if(USE_LUA)
list(APPEND SOURCE_FILES engines/lua.c)
list(APPEND TEST_FILES test/lua.c)
endif()
source_group("Scripting" FILES ${SOURCE_FILES})
source_group("Scripting tests" FILES ${TEST_FILES})

91
src/script/engines/lua.c Normal file
View File

@ -0,0 +1,91 @@
/* Copyright (c) 2013-2022 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/script/lua.h>
#include <lauxlib.h>
static struct mScriptEngineContext* _luaCreate(struct mScriptEngine2*, struct mScriptContext*);
static void _luaDestroy(struct mScriptEngineContext*);
static bool _luaLoad(struct mScriptEngineContext*, struct VFile*, const char** error);
struct mScriptEngineContextLua {
struct mScriptEngineContext d;
lua_State* lua;
};
static struct mScriptEngineLua {
struct mScriptEngine2 d;
} _engineLua = {
.d = {
.name = "lua-" LUA_VERSION_ONLY,
.init = NULL,
.deinit = NULL,
.create = _luaCreate
}
};
struct mScriptEngine2* const mSCRIPT_ENGINE_LUA = &_engineLua.d;
struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mScriptContext* context) {
UNUSED(engine);
struct mScriptEngineContextLua* luaContext = calloc(1, sizeof(*luaContext));
luaContext->d = (struct mScriptEngineContext) {
.context = context,
.destroy = _luaDestroy,
.load = _luaLoad,
};
luaContext->lua = luaL_newstate();
return &luaContext->d;
}
void _luaDestroy(struct mScriptEngineContext* ctx) {
struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) ctx;
lua_close(luaContext->lua);
free(luaContext);
}
#define LUA_BLOCKSIZE 0x1000
struct mScriptEngineLuaReader {
struct VFile* vf;
char block[LUA_BLOCKSIZE];
};
static const char* _reader(lua_State* lua, void* context, size_t* size) {
UNUSED(lua);
struct mScriptEngineLuaReader* reader = context;
ssize_t s = reader->vf->read(reader->vf, reader->block, sizeof(reader->block));
if (s < 0) {
return NULL;
}
*size = s;
return reader->block;
}
bool _luaLoad(struct mScriptEngineContext* ctx, struct VFile* vf, const char** error) {
struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) ctx;
struct mScriptEngineLuaReader data = {
.vf = vf
};
int ret = lua_load(luaContext->lua, _reader, &data, NULL, "t");
switch (ret) {
case LUA_OK:
if (error) {
*error = NULL;
}
return true;
case LUA_ERRSYNTAX:
if (error) {
*error = "Syntax error";
}
break;
default:
if (error) {
*error = "Unknown error";
}
break;
}
return false;
}

65
src/script/test/lua.c Normal file
View File

@ -0,0 +1,65 @@
/* Copyright (c) 2013-2022 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "util/test/suite.h"
#include <mgba/internal/script/lua.h>
M_TEST_SUITE_SETUP(mScriptLua) {
if (mSCRIPT_ENGINE_LUA->init) {
mSCRIPT_ENGINE_LUA->init(mSCRIPT_ENGINE_LUA);
}
return 0;
}
M_TEST_SUITE_TEARDOWN(mScriptLua) {
if (mSCRIPT_ENGINE_LUA->deinit) {
mSCRIPT_ENGINE_LUA->deinit(mSCRIPT_ENGINE_LUA);
}
return 0;
}
M_TEST_DEFINE(create) {
struct mScriptContext context;
mScriptContextInit(&context);
struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context);
lua->destroy(lua);
mScriptContextDeinit(&context);
}
M_TEST_DEFINE(loadGood) {
struct mScriptContext context;
mScriptContextInit(&context);
struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context);
const char* program = "-- test\n";
struct VFile* vf = VFileFromConstMemory(program, strlen(program));
const char* error = NULL;
assert_true(lua->load(lua, vf, &error));
assert_null(error);
lua->destroy(lua);
mScriptContextDeinit(&context);
}
M_TEST_DEFINE(loadSyntax) {
struct mScriptContext context;
mScriptContextInit(&context);
struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context);
const char* program = "Invalid syntax! )\n";
struct VFile* vf = VFileFromConstMemory(program, strlen(program));
const char* error = NULL;
assert_false(lua->load(lua, vf, &error));
assert_non_null(error);
lua->destroy(lua);
mScriptContextDeinit(&context);
}
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua,
cmocka_unit_test(create),
cmocka_unit_test(loadGood),
cmocka_unit_test(loadSyntax))