Module:User:Theknightwho/3

local export = {}

local pe = require("Module:utilities").pattern_escape

local types = { "nil", "boolean", "number", "string", "function", "table", } for k, v in ipairs(types) do	types[k] = nil types[string.char(k)] = v	types[v] = string.char(k) end

local common_keys = { "from", "to", "remove_diacritics", "type", "ancestors", "wikimedia_codes", "wikipedia_article", "translit", "link_tr", "display_text", "entry_name", "sort_key", "dotted_dotless_i", "standardChars" } for k, v in ipairs(common_keys) do	common_keys[k] = nil common_keys[string.char(k + 13)] = v	common_keys[v] = string.char(k + 13) end

local convert = { ["nil"] = function return nil end, ["boolean"] = function(s) if s == "true" then return true elseif s == "false" then return false else return nil end end, ["number"] = tonumber, ["string"] = tostring, } function convert.table(t) local level = t:match("[\245-\255]") local ret = {} local k_default, v_default = t:match("\7?([\1-\6]?)\8?([\1-\6]?)$") k_default = ((k_default ~= "") and types[k_default]) or "number" v_default = ((v_default ~= "") and types[v_default]) or "string" t = t:gsub("[\7\8%z][^\245-\255]+$", "") for k_type, k, v_type, v in t:gmatch("%f[^%z\245-\255]([\1-\6]?)([^" .. level .. "]*)" .. level .. "([\1-\6]?)([^" .. level .. "]*)" .. level) do		if types[k_type] == "nil" or types[k_type] == "function" then error("Key cannot be " .. types[k_type] .. ".") elseif types[v_type] == "function" then error("Value cannot be " .. types[v_type] .. ".") else if types[k_type] == "table" then k = convert.table(k) elseif k_type ~= "" then k = convert[types[k_type]](k) or k			else k = convert[k_default](k) or k			end if types[v_type] == "table" then v = convert.table(v) elseif v_type ~= "" then v = convert[types[v_type]](v) or v			else v = convert[v_default](v) or v			end ret[k] = v		end end return ret end

function export.serialize(data) local serial = {} local freq_counts = {} local freq_order = {} local shortcuts = {} local lookup = {} local function s(data, level) local data_serial = {} local level = level or 0 local level_char = string.char(255 - level) local default_counts = {{}, {}} local max_types = {} for k, v in pairs(data) do			local function insert(str, k_or_v) local typ = type(str) table.insert(data_serial, types[typ]) if typ == "table" then table.insert(data_serial, s(str, level + 1)) else str = tostring(str) table.insert(data_serial, str) if str == "true" or str == "false" or str:find("^%d+$") or str:find("^[Nn][Aa][Nn]$") then k_or_v = (k_or_v == "k" and 1) or 2 default_counts[k_or_v][typ] = (default_counts[k_or_v][typ] or 0) + 1 if #tostring(str) > 1 then freq_counts[types[typ] .. str] = (freq_counts[types[typ] .. str] or 0) + 1 end elseif #tostring(str) > 1 then freq_counts[str] = (freq_counts[str] or 0) + 1 end end table.insert(data_serial, level_char) end insert(k, "k") insert(v, "v") end if #data ~= 0 then table.insert(data_serial, "\0" .. #data) end data_serial = table.concat(data_serial) for k_or_v, count in ipairs(default_counts) do			local max, max_type = 0 for type, c in pairs(count) do				if (					c > max or					(k_or_v == 1 and type == "number" and c == max) or					(k_or_v == 2 and type == "string" and c == max)				) then max = c					max_type = type end end if k_or_v == 1 then max_types[k_or_v] = max_type or "number" else max_types[k_or_v] = max_type or "string" end end data_serial = data_serial:gsub("%f[^%z\245-\255]([\1-\6])([^\7\8" .. level_char .. "]*)(" .. level_char .. ")([\1-\6])([^" .. level_char .. "]*)(" .. level_char .. ")", function(m1, m2, m3, m4, m5, m6)			local function check_type(mm1, mm2, n)				if mm1 == types[max_types[n]] or not (types[mm1] == "table" or mm2 == "true" or mm2 == "false" or mm2:find("^%d+$")) or mm2:find("^[Nn][Aa][Nn]$") then					return ""				end				return mm1			end			m1 = check_type(m1, m2, 1)			m4 = check_type(m4, m5, 2)			return m1 .. m2 .. m3 .. m4 .. m5 .. m6		end) if max_types[1] ~= "number" then data_serial = (data_serial .. "\7" .. types[max_types[1]]) end if max_types[2] ~= "string" then data_serial = (data_serial .. "\8" .. types[max_types[2]]) end return data_serial end local data = s(data) for str, freq in pairs(freq_counts) do table.insert(freq_order, freq .. "\0" .. str) freq_counts[str] = nil end -- Manual garbage collection. freq_counts[1] = true freq_counts = nil table.sort(freq_order, function(a, b)		return (tonumber(a:match("^%d+")) - 1) * a:match("%z(.*)$"):len > (tonumber(b:match("^%d+")) - 1) * b:match("%z(.*)$"):len	end) for k, v in ipairs(freq_order) do		local str = v:match("%z(.*)$") local weight = (tonumber(v:match("^%d+")) - 1) * str:len if k <= 18 and weight > 1 then shortcuts[str] = string.char(k + 13) elseif k <= 254 and weight > 2 then shortcuts[str] = "\192" .. string.char(k - 10) elseif k <= 490 and weight > 2 then shortcuts[str] = "\193" .. string.char(k - 246) else break end table.insert(lookup, shortcuts[str]) table.insert(lookup, str) freq_order[k] = nil end -- Manual garbage collection. freq_order[1] = true freq_order = nil for k, v in pairs(shortcuts) do		data = data:gsub(pe(k), v)	end data = data .. "\0\0\0\0" .. table.concat(lookup) return (data:gsub(".", function(m) local byte = m:byte if byte < 0x20 then return "\\" .. ("%03d"):format(byte) elseif byte == 0x22 or byte == 0x5C then return "\\" .. m		elseif byte >= 0x7F then return "\\" .. byte end end)	:gsub("%s*$", function(m) return m:gsub(".", function(byte)			local byte = m:byte			return "\\" .. ("%03d"):format(byte)		end) end)) end

function export.get_value(t, ...) local raw if t == "raw" then raw = true t = select(1, ...) end local n, v, v_type = select("#", ...) for i = (raw and 2 or 1), n do		local k = select(i, ...) local level = t:match("[\245-\255]") local k_type = types[type(k)] k = types[k_type] == "string" and pe(k) or k v = t:match(k_type .. k .. "(%b" .. level .. level .. ")")		if not v then return nil else v, v_type = v:gsub(level, "") end v_type, v = v:match("^([\1-\6]?)(.*)") v_type = types[v_type] if i < n then t = v		end end if raw and v_type == "table" then return v	else return convert[v_type](v) end end

function export.get_len(t, ...) if select("#", ...) > 0 then t = export.get_value("raw", t, ...) end return tonumber(t:match("%z(%d+)$")) or 0 end

export.make_table = convert.table

return export