Module:User:Benwing2/category tree/poscatboiler/data/scripts/blocks
Appearance
- This module sandbox lacks a documentation subpage. You may create it.
- Useful links: root page • root page’s subpages • links • transclusions • testcases • user page • user talk page • userspace
This is a private module sandbox of Benwing2, for their own experimentation. Items in this module may be added and removed at Benwing2's discretion; do not rely on this module's stability.
local export = {}
local fun = require "Module:fun"
local Array = require "Module:array"
local function compare_script_codes(code1, code2)
-- Sort four-letter codes and non-four-letter codes alphabetically.
if (#code1 == 4) == (#code2 == 4) then
return code1 < code2
-- Put four-letter codes before non-four-letter codes.
else
return #code1 == 4
end
end
local function sort_scripts(script_codes)
table.sort(script_codes, compare_script_codes)
return script_codes
end
local block_data = require "Module:Unicode data/blocks"
-- Add position of range in the array of blocks to the range tables.
for i, range in ipairs(block_data) do
range[4] = i
end
-- Binary search, to avoid iterating over entire table in order to look up the
-- higher codepoints.
local floor = math.floor
local function binary_lookup_block(codepoint)
local iStart, iEnd = 1, block_data.length or #block_data
while iStart <= iEnd do
local iMid = floor((iStart + iEnd) / 2)
local range = block_data[iMid]
if codepoint < range[1] then
iEnd = iMid - 1
elseif codepoint <= range[2] then
return range
else
iStart = iMid + 1
end
end
error(string.format("No block found for codepoint U+%04X.", codepoint))
end
function export.lookup_block(char)
local codepoint = mw.ustring.codepoint(char)
local range = binary_lookup_block(codepoint)
if range then
return range
else
error(string.format("No block found for U+%04X (%s).", codepoint, mw.ustring.char(codepoint)))
end
end
function export.get_singles_and_ranges(pattern)
local ranges, singles = {}, {}
pattern = mw.ustring.gsub(
pattern,
"(.)%-(.)",
function(lower, higher)
table.insert(ranges, { lower, higher })
return ""
end)
for character in mw.ustring.gmatch(pattern, ".") do
table.insert(singles, character)
end
return singles, ranges
end
function export.get_block_arrays(pattern)
local singles, ranges = export.get_singles_and_ranges(pattern)
local blocks = {}
for _, character in ipairs(singles) do
blocks[export.lookup_block(character)] = true
end
for _, range in ipairs(ranges) do
local block_array1, block_array2 = export.lookup_block(range[1]), export.lookup_block(range[2])
for i = block_array1[4], block_array2[4] do
blocks[block_data[i]] = true
end
end
return Array.keysToList(blocks, function (block1, block2) return block1[4] < block2[4] end)
end
local function format_block_info(block_array)
return ("[[Appendix:Unicode/%s|%s]] (U+%04X–U+%04X)"):format(block_array[3], block_array[3], block_array[1], block_array[2])
end
function export.print_blocks(block_arrays, prefix)
table.sort(
block_arrays,
function (block_array1, block_array2)
return block_array1[1] < block_array2[1]
end)
local block_names = fun.map(
function (block_array)
return "* " .. format_block_info(block_array)
end,
block_arrays)
if prefix then
table.insert(block_names, 1, prefix)
end
return table.concat(block_names, "\n")
end
function export.print_blocks_by_canonical_name(script_name)
if type(script_name) ~= "string" then
error("script_name should be a string, not " .. type(script_name) .. ".")
end
local scripts_by_pattern = {}
setmetatable(
scripts_by_pattern,
{
__index = function(self, key)
if key == nil then
return
end
local val = Array()
self[key] = val
return val
end
})
local count = 0
for code, data in pairs(mw.loadData("Module:scripts/data")) do
if data.canonicalName == script_name and data.characters then
count = count + 1
scripts_by_pattern[data.characters]:insert(code)
end
end
if not next(scripts_by_pattern) then
return nil
end
local block_arrays_by_scripts = {}
local block_count = 0
-- Construct arrays of blocks and count the blocks.
for pattern, scripts in pairs(scripts_by_pattern) do
local array = export.get_block_arrays(pattern)
block_arrays_by_scripts[sort_scripts(scripts)] = array
block_count = block_count + #array
end
require("Module:debug").track{
"scriptcatboiler/blocks/" .. count,
"scriptcatboiler/blocks/" .. block_count
}
if count == 1 and block_count == 1 then
local scripts, block_arrays = next(block_arrays_by_scripts)
if scripts[2] or block_arrays[2] then
error("More than one script or more than one block. Something is wrong.")
end
return ("The characters of <code>%s</code> are found in the Unicode block %s")
:format(scripts[1], format_block_info(block_arrays[1]))
else
local collapsible1 = '{|\n'
local collapsible3 = '</div></div>'
return '{| class="mw-collapsible mw-collapsed wikitable" style="width: 30em;"\n|+ style="font-weight: normal;" | ' .. "'''Unicode block"
.. (block_count > 1 and "s" or "") .. " for characters in "
.. (count > 1 and "these scripts" or "this script") .. "'''\n|\n"
.. table.concat(
fun.mapIter(
function(block_arrays, scripts)
return export.print_blocks(
block_arrays,
"; Block" .. (block_arrays[2] and "s" or "") .. " in "
.. table.concat(
fun.map(
function (script_code)
return "<code>" .. script_code .. "</code>"
end,
scripts),
", "))
end,
require("Module:table").sortedPairs(
block_arrays_by_scripts,
function(script_array1, script_array2)
return compare_script_codes(script_array1[1], script_array2[1])
end)),
"\n")
.. '\n|}'
end
end
-- For testing.
function export.print_blocks_by_canonical_name_template(frame)
return export.print_blocks_by_canonical_name(frame.args[1])
end
return export