diff --git a/changelog.md b/changelog.md index eb58387bbd8..1829a94ae3d 100644 --- a/changelog.md +++ b/changelog.md @@ -34,6 +34,7 @@ Japanese, Polish, Scottish Gaelic, Slovak, Spanish, Ukrainian. ### Lua API * Allow specifying custom flags (in particular teleport) when using a custom cost function in wesnoth.find_path + * Add wml.load() and wml.parse() functions ### User Interface * Don't show in the sidebar the time of day schedule of a shrouded hex. (issue #3638) ### Packaging diff --git a/data/lua/core.lua b/data/lua/core.lua index 0542d10b789..2df1ac0604f 100644 --- a/data/lua/core.lua +++ b/data/lua/core.lua @@ -19,7 +19,6 @@ end --[========[Config Manipulation Functions]========] -wml = {} wml.tovconfig = wesnoth.tovconfig wml.tostring = wesnoth.debug diff --git a/src/scripting/lua_kernel_base.cpp b/src/scripting/lua_kernel_base.cpp index a806344fb56..5f9abb6c857 100644 --- a/src/scripting/lua_kernel_base.cpp +++ b/src/scripting/lua_kernel_base.cpp @@ -41,6 +41,9 @@ #include "formula/string_utils.hpp" #include "serialization/string_utils.hpp" +#include "serialization/schema_validator.hpp" +#include "serialization/parser.hpp" +#include "serialization/preprocessor.hpp" #include "utils/functional.hpp" #include "utils/name_generator.hpp" #include "utils/markov_generator.hpp" @@ -53,6 +56,7 @@ #include #include #include +#include #include "lua/lauxlib.h" #include "lua/lua.h" @@ -395,6 +399,71 @@ static int intf_debug(lua_State* L) { return 1; } +/** + * Loads a WML file into a config + * - Arg 1: WML file path + * - Arg 2: (optional) Array of preprocessor defines, or false to skip preprocessing (true is also valid) + * - Arg 3: (optional) Path to a schema file for validation (omit for no validation) + * - Ret: config + */ +static int intf_load_wml(lua_State* L) +{ + std::string file = luaL_checkstring(L, 1); + bool preprocess = true; + preproc_map defines_map; + if(lua_type(L, 2) == LUA_TBOOLEAN) { + preprocess = luaW_toboolean(L, 2); + } else if(lua_type(L, 2) == LUA_TTABLE || lua_type(L, 2) == LUA_TUSERDATA) { + lua_len(L, 2); + int n = lua_tonumber(L, -1); + lua_pop(L, 1); + for(int i = 0; i < n; i++) { + lua_geti(L, 2, i); + if(!lua_isstring(L, -1)) { + return luaL_argerror(L, 2, "expected bool or array of strings"); + } + std::string define = lua_tostring(L, -1); + lua_pop(L, 1); + if(!define.empty()) { + defines_map.emplace(define, preproc_define(define)); + } + } + } else if(!lua_isnoneornil(L, 2)) { + return luaL_argerror(L, 2, "expected bool or array of strings"); + } + std::string schema_path = luaL_optstring(L, 3, ""); + std::shared_ptr validator; + if(!schema_path.empty()) { + validator.reset(new schema_validation::schema_validator(filesystem::get_wml_location(schema_path))); + validator->set_create_exceptions(false); // Don't crash if there's an error, just go ahead anyway + } + std::string wml_file = filesystem::get_wml_location(file); + filesystem::scoped_istream stream; + config result; + if(preprocess) { + stream = preprocess_file(wml_file, &defines_map); + } else { + stream.reset(new std::ifstream(wml_file)); + } + read(result, *stream, validator.get()); + luaW_pushconfig(L, result); + return 1; +} + +/** + * Parses a WML string into a config; does not preprocess or validate + * - Arg 1: WML string + * - Ret: config + */ +static int intf_parse_wml(lua_State* L) +{ + std::string wml = luaL_checkstring(L, 1); + config result; + read(result, wml, nullptr); + luaW_pushconfig(L, result); + return 1; +} + // End Callback implementations // Template which allows to push member functions to the lua kernel base into lua as C functions, using a shim @@ -539,6 +608,15 @@ lua_kernel_base::lua_kernel_base() luaL_setfuncs(L, callbacks, 0); //lua_cpp::set_functions(L, cpp_callbacks, 0); lua_setglobal(L, "wesnoth"); + + static luaL_Reg const wml_callbacks[]= { + { "load", &intf_load_wml}, + { "parse", &intf_parse_wml}, + { nullptr, nullptr }, + }; + lua_newtable(L); + luaL_setfuncs(L, wml_callbacks, 0); + lua_setglobal(L, "wml"); // Override the print function cmd_log_ << "Redirecting print function...\n";