Add stringx module that exposes Wesnoth's split/join functions to Lua
The built-in string module is set as the metatable index for the stringx module, and the stringx module is set as the metatable index for strings, meaning all string and stringx methods are available through (''):method_name. This also enables direct indexing of strings to get individual characters (ie ('str')[1] and the like), which package.lua already assumed even though it didn't work. Negative indices are supported to, and index from the end of the srring. New functions: - stringx.split - stringx.parenthetical_split - stringx.map_split - stringx.escaped_split - stringx.quoted_split - stringx.anim_split - stringx.join - stringx.join_map
This commit is contained in:
parent
86f66a5825
commit
aa5ac6af29
3 changed files with 235 additions and 4 deletions
|
@ -16,6 +16,25 @@ function gui.confirm(title, msg)
|
|||
return gui.show_prompt(title, msg, "yes_no", true)
|
||||
end
|
||||
|
||||
--[========[Additional string support functions]========]
|
||||
|
||||
function stringx.escaped_split(str, sep, esc)
|
||||
esc = esc or '\\'
|
||||
return stringx.split(str, sep, {escape = esc})
|
||||
end
|
||||
|
||||
function stringx.quoted_split(str, sep, left, right)
|
||||
right = right or left
|
||||
if left == nil and right == nil then
|
||||
left = '('
|
||||
right = ')'
|
||||
end
|
||||
return stringx.split(str, sep, {quote_left = left, quote_right = right})
|
||||
end
|
||||
|
||||
function stringx.anim_split(str, sep)
|
||||
return stringx.split(str, sep, {expand_anim = true});
|
||||
end
|
||||
|
||||
--[========[Config Manipulation Functions]========]
|
||||
|
||||
|
|
|
@ -958,10 +958,7 @@ bool luaW_checkvariable(lua_State *L, variable_access_create& v, int n)
|
|||
|
||||
bool luaW_tableget(lua_State *L, int index, const char* key)
|
||||
{
|
||||
if(index < 0) {
|
||||
//with the next lua_pushstring negative indicies will no longer be correct.
|
||||
--index;
|
||||
}
|
||||
index = lua_absindex(L, index);
|
||||
lua_pushstring(L, key);
|
||||
lua_gettable(L, index);
|
||||
if(lua_isnoneornil(L, -1)) {
|
||||
|
|
|
@ -440,6 +440,196 @@ static int intf_format_list(lua_State* L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int impl_str_index(lua_State* L)
|
||||
{
|
||||
if(lua_type(L, 2) == LUA_TSTRING) {
|
||||
// return stringx[key]
|
||||
lua_getglobal(L, "stringx");
|
||||
lua_pushvalue(L, 2);
|
||||
lua_gettable(L, -2);
|
||||
return 1;
|
||||
} else if(lua_type(L, 2) == LUA_TNUMBER) {
|
||||
// return string.sub(str, key, key)
|
||||
luaW_getglobal(L, "string", "sub");
|
||||
lua_pushvalue(L, 1);
|
||||
lua_pushvalue(L, 2);
|
||||
lua_pushvalue(L, 2);
|
||||
lua_call(L, 3, 1);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intf_str_split(lua_State* L)
|
||||
{
|
||||
enum {BASIC, ESCAPED, PAREN, ANIM} type = BASIC;
|
||||
const std::string& str = luaL_checkstring(L, 1);
|
||||
const std::string& sep = luaL_optstring(L, 2, ",");
|
||||
std::string left, right;
|
||||
int flags = utils::REMOVE_EMPTY | utils::STRIP_SPACES;
|
||||
if(lua_istable(L, 3)) {
|
||||
flags = 0;
|
||||
if(luaW_table_get_def(L, 3, "remove_empty", true)) {
|
||||
flags |= utils::REMOVE_EMPTY;
|
||||
}
|
||||
if(luaW_table_get_def(L, 3, "strip_spaces", true)) {
|
||||
flags |= utils::STRIP_SPACES;
|
||||
}
|
||||
bool anim = luaW_table_get_def(L, 3, "expand_anim", false);
|
||||
if(luaW_tableget(L, 3, "escape")) {
|
||||
if(anim) {
|
||||
return luaL_error(L, "escape and expand_anim options are incompatible!");
|
||||
}
|
||||
type = ESCAPED;
|
||||
left = luaL_checkstring(L, -1);
|
||||
if(left.size() != 1) {
|
||||
return luaL_error(L, "escape must be a single character");
|
||||
}
|
||||
} else if(luaW_tableget(L, 3, "quote")) {
|
||||
left = right = luaL_checkstring(L, -1);
|
||||
if(anim) {
|
||||
type = ANIM;
|
||||
left.push_back('[');
|
||||
right.push_back(']');
|
||||
} else type = PAREN;
|
||||
} else if(luaW_tableget(L, 3, "quote_left") && luaW_tableget(L, 3, "quote_right")) {
|
||||
left = luaL_checkstring(L, -2);
|
||||
right = luaL_checkstring(L, -1);
|
||||
if(anim) {
|
||||
if(left.find_first_of("[]") != std::string::npos || right.find_first_of("[]") != std::string::npos) {
|
||||
return luaL_error(L, "left and right cannot include square brackets [] if expand_anim is enabled");
|
||||
}
|
||||
type = ANIM;
|
||||
left.push_back('[');
|
||||
right.push_back(']');
|
||||
} else type = PAREN;
|
||||
} else if(anim) {
|
||||
type = ANIM;
|
||||
left = "([";
|
||||
right = ")]";
|
||||
}
|
||||
if(type != ESCAPED && left.size() != right.size()) {
|
||||
return luaL_error(L, "left and right need to be strings of the same length");
|
||||
}
|
||||
}
|
||||
switch(type) {
|
||||
case BASIC:
|
||||
lua_push(L, utils::split(str, sep[0], flags));
|
||||
break;
|
||||
case ESCAPED:
|
||||
lua_push(L, utils::quoted_split(str, sep[0], flags, left[0]));
|
||||
break;
|
||||
case PAREN:
|
||||
lua_push(L, utils::parenthetical_split(str, sep[0], left, right, flags));
|
||||
break;
|
||||
case ANIM:
|
||||
lua_push(L, utils::square_parenthetical_split(str, sep[0], left, right, flags));
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int intf_str_paren_split(lua_State* L)
|
||||
{
|
||||
const std::string& str = luaL_checkstring(L, 1);
|
||||
const std::string& left = luaL_optstring(L, 2, "(");
|
||||
const std::string& right = luaL_optstring(L, 3, ")");
|
||||
if(left.size() != right.size()) {
|
||||
return luaL_error(L, "left and right need to be strings of the same length");
|
||||
}
|
||||
bool strip_spaces = luaL_opt(L, luaW_toboolean, 4, true);
|
||||
lua_push(L, utils::parenthetical_split(str, 0, left, right, strip_spaces ? utils::STRIP_SPACES : 0));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int intf_str_map_split(lua_State* L)
|
||||
{
|
||||
const std::string& str = luaL_checkstring(L, 1);
|
||||
const std::string& sep = luaL_optstring(L, 2, ",");
|
||||
const std::string& kv = luaL_optstring(L, 3, ":");
|
||||
std::string dflt;
|
||||
if(sep.size() != 1) {
|
||||
return luaL_error(L, "separator must be a single character");
|
||||
}
|
||||
if(kv.size() != 1) {
|
||||
return luaL_error(L, "key_value_separator must be a single character");
|
||||
}
|
||||
int flags = utils::REMOVE_EMPTY | utils::STRIP_SPACES;
|
||||
if(lua_istable(L, 4)) {
|
||||
flags = 0;
|
||||
if(luaW_table_get_def(L, 4, "remove_empty", true)) {
|
||||
flags |= utils::REMOVE_EMPTY;
|
||||
}
|
||||
if(luaW_table_get_def(L, 4, "strip_spaces", true)) {
|
||||
flags |= utils::STRIP_SPACES;
|
||||
}
|
||||
if(luaW_tableget(L, 4, "default")) {
|
||||
dflt = luaL_checkstring(L, -1);
|
||||
}
|
||||
}
|
||||
lua_push(L, utils::map_split(str, sep[0], kv[0], flags, dflt));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int intf_str_join(lua_State* L) {
|
||||
// Support both join(list, [sep]) and join(sep, list)
|
||||
// The latter form means sep:join(list) also works.
|
||||
std::string sep;
|
||||
int list_idx;
|
||||
if(lua_istable(L, 1)) {
|
||||
list_idx = 1;
|
||||
sep = luaL_optstring(L, 2, ",");
|
||||
} else if(lua_istable(L, 2)) {
|
||||
sep = luaL_checkstring(L, 1);
|
||||
list_idx = 2;
|
||||
} else return luaL_error(L, "invalid arguments to join, should have map and separator");
|
||||
std::vector<std::string> pieces;
|
||||
for(int i = 1; i <= luaL_len(L, list_idx); i++) {
|
||||
lua_getglobal(L, "tostring");
|
||||
lua_geti(L, list_idx, i);
|
||||
lua_call(L, 1, 1);
|
||||
pieces.push_back(luaL_checkstring(L, -1));
|
||||
}
|
||||
lua_push(L, utils::join(pieces, sep));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int intf_str_join_map(lua_State* L) {
|
||||
// Support join_map(map, [sep], [kv_sep]), join_map(sep, map, [kv_sep]), and join_map(sep, kv_sep, map)
|
||||
// The latter forms mean sep:join_map(kv_sep, map) and sep:join_map(map) also work.
|
||||
// If only one separator is given in the first form, it will be sep, not kv_sep
|
||||
std::string sep, kv;
|
||||
int map_idx;
|
||||
if(lua_istable(L, 1)) {
|
||||
map_idx = 1;
|
||||
sep = luaL_optstring(L, 2, ",");
|
||||
kv = luaL_optstring(L, 3, ":");
|
||||
} else if(lua_istable(L, 2)) {
|
||||
sep = luaL_checkstring(L, 1);
|
||||
map_idx = 2;
|
||||
kv = luaL_optstring(L, 3, ":");
|
||||
} else if(lua_istable(L, 3)) {
|
||||
sep = luaL_checkstring(L, 1);
|
||||
kv = luaL_checkstring(L, 2);
|
||||
map_idx = 3;
|
||||
} else return luaL_error(L, "invalid arguments to join_map, should have map, separator, and key_value_separator");
|
||||
std::map<std::string, std::string> pieces;
|
||||
for(lua_pushnil(L); lua_next(L, map_idx); /*pop in loop body*/) {
|
||||
int key_idx = lua_absindex(L, -2), val_idx = lua_absindex(L, -1);
|
||||
lua_getglobal(L, "tostring");
|
||||
lua_pushvalue(L, key_idx);
|
||||
lua_call(L, 1, 1);
|
||||
std::string& val = pieces[luaL_checkstring(L, -1)];
|
||||
lua_getglobal(L, "tostring");
|
||||
lua_pushvalue(L, val_idx);
|
||||
lua_call(L, 1, 1);
|
||||
val = luaL_checkstring(L, -1);
|
||||
lua_settop(L, key_idx);
|
||||
}
|
||||
lua_push(L, utils::join_map(pieces, sep, kv));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int intf_get_language(lua_State* L)
|
||||
{
|
||||
lua_push(L, get_language().localename);
|
||||
|
@ -675,6 +865,31 @@ lua_kernel_base::lua_kernel_base()
|
|||
luaL_setfuncs(L, callbacks, 0);
|
||||
//lua_cpp::set_functions(L, cpp_callbacks, 0);
|
||||
lua_setglobal(L, "wesnoth");
|
||||
|
||||
cmd_log_ << "Adding stringx module...\n";
|
||||
static luaL_Reg const str_callbacks[] = {
|
||||
{ "split", &intf_str_split },
|
||||
{ "parenthetical_split", &intf_str_paren_split },
|
||||
{ "map_split", &intf_str_map_split },
|
||||
{ "join", &intf_str_join },
|
||||
{ "join_map", &intf_str_join_map },
|
||||
{ nullptr, nullptr },
|
||||
};
|
||||
lua_newtable(L);
|
||||
luaL_setfuncs(L, str_callbacks, 0);
|
||||
// Set the stringx metatable to index the string module
|
||||
lua_createtable(L, 0, 1);
|
||||
lua_getglobal(L, "string");
|
||||
lua_setfield(L, -2, "__index");
|
||||
lua_setmetatable(L, -2);
|
||||
lua_setglobal(L, "stringx");
|
||||
|
||||
// Set the metatable of strings to index the stringx module instead of the string module
|
||||
lua_pushliteral(L, "");
|
||||
lua_getmetatable(L, -1);
|
||||
lua_pushcfunction(L, &impl_str_index);
|
||||
lua_setfield(L, -2, "__index");
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
cmd_log_ << "Adding wml module...\n";
|
||||
static luaL_Reg const wml_callbacks[]= {
|
||||
|
|
Loading…
Add table
Reference in a new issue