Module:array: Difference between revisions
m
(bot) slight optimization to 5.2 compat: prefer unpack to table.unpack
(Created page with "local Array = {} local array_constructor -- Copy table library so as not to unexpectedly change the behavior of code that -- uses it. local array_methods = mw.clone(table) -...") |
m ((bot) slight optimization to 5.2 compat: prefer unpack to table.unpack) |
||
| Line 1: | Line 1: | ||
local | local export = {} | ||
local debug_track_module = "Module:debug/track" | |||
local function_module = "Module:fun" | |||
local | local table_module = "Module:table" | ||
-- | local get_array_mt -- Defined below. | ||
local getmetatable = getmetatable | |||
local ipairs = ipairs | |||
local pairs = pairs | |||
local rawget = rawget | |||
local rawset = rawset | |||
local require = require | |||
local select = select | |||
local setmetatable = setmetatable | |||
local sort = table.sort | |||
local type = type | |||
local unpack = unpack or table.unpack -- Lua 5.2 compatibility | |||
local upper = string.upper | |||
-- | local array_mt -- Defined below. | ||
function | --[==[ | ||
Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] | |||
local function append(...) | |||
end | append = require(table_module).append | ||
return append(...) | |||
function | end | ||
local function debug_track(...) | |||
debug_track = require(debug_track_module) | |||
return debug_track(...) | |||
end | end | ||
local function deep_copy(...) | |||
deep_copy = require(table_module).deepCopy | |||
return deep_copy(...) | |||
end | end | ||
local function list_to_set(...) | |||
list_to_set = require(table_module).listToSet | |||
return list_to_set(...) | |||
end | end | ||
local | local function shallow_copy(...) | ||
shallow_copy = require(table_module).shallowCopy | |||
return shallow_copy(...) | |||
end | end | ||
-- | --[==[ | ||
Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==] | |||
local m_function | |||
local function get_m_function() | |||
m_function, get_m_function = require(function_module), nil | |||
local function | return m_function | ||
end | |||
local | local m_table | ||
local | local function get_m_table() | ||
m_table, get_m_table = require(table_module), nil | |||
return m_table | |||
end | end | ||
-- Functions from [[Module:table]] that operate on arrays or sparse arrays. | |||
-- Functions from [[Module:table]] that operate on arrays or sparse arrays | |||
-- List copied from [[Module:table/documentation]]. | -- List copied from [[Module:table/documentation]]. | ||
"removeDuplicates", " | local m_table_array_funcs | ||
local function get_m_table_array_funcs() | |||
m_table_array_funcs = list_to_set{ | |||
-- non-sparse | |||
"signedIndex", "append", "extend", "extendIfNot", "slice", "removeDuplicates", "length", | |||
"size", "contains", "serialCommaJoin", "reverseIpairs", "reverse", | |||
} | "invert", "listToSet", "flatten", "isArray", | ||
-- sparse | |||
-- Functions from [[Module:fun]] that take an array in the second argument. | "numKeys", "maxIndex", "compressSparseArray", "indexPairs", "indexIpairs", | ||
-- They just have to have the argument order reversed to work as methods of the | "sparseIpairs", | ||
-- array object. | -- tables in general | ||
local | "shallowCopy", "deepCopy" | ||
} | |||
get_m_table_array_funcs = nil | |||
return m_table_array_funcs | |||
end | |||
-- Functions from [[Module:fun]] that take an array in the second argument. | |||
-- They just have to have the argument order reversed to work as methods of the | |||
-- array object. | |||
local m_function_array_funcs | |||
local function get_m_function_array_funcs() | |||
m_function_array_funcs = list_to_set{ | |||
"map", "some", "all", "filter", "fold" | |||
} | |||
get_m_function_array_funcs = nil | |||
return m_function_array_funcs | |||
end | |||
-- Functions from [[Module:table]] that create an array or table. | |||
-- Not all of these operate on arrays. | |||
local m_table_new_array_funcs | |||
local function get_m_table_new_array_funcs() | |||
m_table_new_array_funcs = list_to_set{ | |||
-- Array. | |||
"append", "slice", "removeDuplicates", "numKeys", "compressSparseArray", | |||
"keysToList", "reverse", "flatten", | |||
-- Array or table. | |||
"shallowCopy", "deepCopy" | |||
} | |||
get_m_table_new_array_funcs = nil | |||
return m_table_new_array_funcs | |||
end | |||
-- Functions from [[Module:fun]] that create an array or table. | |||
-- Not all of these operate on arrays. | |||
local m_function_new_array_funcs | |||
local function get_m_function_new_array_funcs() | |||
m_function_new_array_funcs = list_to_set{ | |||
"map", "filter", | |||
} | |||
get_m_function_new_array_funcs = nil | |||
return m_function_new_array_funcs | |||
end | |||
-- Add aliases for the functions from [[Module:table]] whose names | -- Add aliases for the functions from [[Module:table]] whose names | ||
-- contain "array" or "list", which is redundant | -- contain "array" or "list", which is redundant. | ||
-- The key redirects to the value. | -- The key redirects to the value. | ||
local alias_of = { | local alias_of = { | ||
compress = "compressSparseArray", keys = "keysToList", toSet = "listToSet | compress = "compressSparseArray", | ||
keys = "keysToList", | |||
toSet = "listToSet", | |||
} | } | ||
local function underscore_to_camel_case(str) | |||
if type(str) ~= "string" then | |||
return str | |||
end | |||
local ret = str:gsub("_(.)", upper) | |||
if ret ~= str then | |||
debug_track("array/underscore to camel case") | |||
end | |||
return ret | |||
end | |||
local function get_module_function(key, module, module_name) | local function get_module_function(key, module, module_name) | ||
return module[key] | return module[key] or | ||
error(("Cannot find %s in [[Module:%s]]"):format(mw.dumpObject(key), module_name)) | |||
end | end | ||
local function wrap_in_array_constructor(func) | local function wrap_in_array_constructor(func) | ||
return function (...) | return function (...) | ||
return | return setmetatable(func(...), array_mt or get_array_mt()) | ||
end | end | ||
end | end | ||
function get_array_mt() | |||
-- Copy table library so as not to unexpectedly change the behavior of code that | |||
-- uses it. | |||
local Array = deep_copy(table) | |||
local | Array.ipairs = ipairs | ||
Array.pairs = pairs | |||
Array.unpack = unpack | |||
Array.listToText = mw.text.listToText | |||
-- Create version of table.sort that returns the table. | |||
function Array:sort(comp) | |||
sort(self, comp) | |||
return self | |||
function Array: | |||
return | |||
end | end | ||
function Array:type() | |||
local mt = getmetatable(self) | |||
return mt and type(mt) == "table" and rawget(mt, "__type") or nil | |||
return | |||
end | end | ||
local Array_mt = {} | |||
setmetatable(Array, Array_mt) | |||
function Array_mt:__index(key) | |||
if type(key) ~= "string" then | |||
return nil | |||
if | |||
end | end | ||
local raw_func = | -- Convert underscores to camel case: num_keys -> numKeys. | ||
-- FIXME: this is pointless overhead: remove once nothing relies on it. | |||
key = underscore_to_camel_case(key) | |||
key = alias_of[key] or key | |||
local func = rawget(self, key) | |||
if func ~= nil then | |||
return func | |||
elseif (m_table_array_funcs or get_m_table_array_funcs())[key] then | |||
func = get_module_function(key, m_table or get_m_table(), "table") | |||
if (m_table_new_array_funcs or get_m_table_new_array_funcs())[key] then | |||
func = wrap_in_array_constructor(func) | |||
end | |||
elseif (m_function_array_funcs or get_m_function_array_funcs())[key] then | |||
local raw_func = get_module_function(key, m_function or get_m_function(), "fun") | |||
--[==[ Once isArray is no longer used: | |||
function func(a, b, ...) | |||
return raw_func(b, a, ...) | |||
end | |||
]==] | |||
if key == "fold" then | |||
function func(t, f, accum) | |||
return raw_func(f, t, accum) | |||
end | |||
else | |||
function func(a, b) -- TODO: isArray parameter is probably unnecessary, and doesn't work with sparse arrays anyway. | |||
debug_track("array/isArray") | |||
return raw_func(b, a, "isArray") | |||
end | |||
end | |||
if (m_function_new_array_funcs or get_m_function_new_array_funcs())[key] then | |||
func = wrap_in_array_constructor(func) | |||
end | |||
else | else | ||
return nil | |||
end | end | ||
rawset(Array, key, func) | |||
return func | |||
end | end | ||
array_mt = { | |||
__index = Array, | |||
return | __type = "array", | ||
} | |||
function array_mt:__add(v) | |||
return setmetatable(append(self, v), array_mt or get_array_mt()) | |||
end | end | ||
get_array_mt = nil | |||
return array_mt | |||
end | end | ||
function | -- A function to convert string key-table modules such | ||
-- as [[Module:languages/data/2]] into arrays. | |||
-- "from" is a bad name. | |||
-- field_for_key supplies the field name in which the | |||
-- key will be stored. | |||
function export.from(map, field_for_key) | |||
local arr, i = {}, 0 | |||
for key, val in pairs(map) do | |||
i = i + 1 | |||
local new_val = shallow_copy(val) | |||
if field_for_key then | |||
new_val[field_for_key] = key | |||
end | end | ||
arr[i] = new_val | |||
end | end | ||
return setmetatable(arr, array_mt or get_array_mt()) | |||
end | end | ||
function | local export_mt = {} | ||
function export_mt:__call(...) | |||
local arr | local arr | ||
if select("#", ...) == 1 and type((...)) == "table" then | if select("#", ...) == 1 and type((...)) == "table" then | ||
arr = ... | arr = ... | ||
local mt = getmetatable(arr) | local mt = getmetatable(arr) | ||
-- If table has been loaded with mw.loadData, copy it to avoid the | -- If table has been loaded with mw.loadData, copy it to avoid the | ||
-- limitations of it being a virtual table. | -- limitations of it being a virtual table. | ||
if mt and mt | if mt and type(mt) == "table" and rawget(mt, "mw_loadData") == true then | ||
arr = shallow_copy(arr) | |||
arr = | |||
end | end | ||
else | else | ||
arr = { ... } | arr = {...} | ||
end | end | ||
return setmetatable(arr, | return setmetatable(arr, array_mt or get_array_mt()) | ||
end | end | ||
function export_mt:__index(key) | |||
function | -- Convert underscores to camel case: num_keys -> numKeys. | ||
-- FIXME: this is pointless overhead: remove once nothing relies on it. | |||
key = underscore_to_camel_case(key) | key = underscore_to_camel_case(key) | ||
key = alias_of[key] or key | key = alias_of[key] or key | ||
if | local func = rawget(self, key) | ||
if func ~= nil then | |||
return func | |||
elseif (m_table_new_array_funcs or get_m_table_new_array_funcs())[key] then | |||
func = get_module_function(key, m_table or get_m_table(), "table") | |||
elseif (m_function_new_array_funcs or get_m_function_new_array_funcs())[key] then | |||
func = get_module_function(key, m_function or get_m_function(), "fun") | |||
else | |||
return nil | |||
end | end | ||
func = wrap_in_array_constructor(func) | |||
rawset(export, key, func) | |||
return func | |||
end | end | ||
return | return setmetatable(export, export_mt) | ||