Документация
-- Модуль для создания таблиц с симолами Unicode
local p = {}

local getArgs = require('Module:Arguments').getArgs
local unicodeData = require('Module:Unicode/data').getData

local str_char = string.char
local tbl_concat = table.concat
local txt_split = mw.text.split
local txt_trim = mw.text.trim

local bytemarkers = { {0x7FF,192}, {0xFFFF,224}, {0x1FFFFF,240} }

local function utf8(decimal)
	if decimal < 128 then
		return str_char(decimal)
	end
	
	local charbytes = {}
	
	for bytes, vals in ipairs(bytemarkers) do
		if decimal <= vals[1] then
			for b = bytes + 1, 2, -1 do
				local mod = decimal % 64
				decimal = (decimal - mod) / 64
				charbytes[b] = str_char(128 + mod)
			end
			charbytes[1] = str_char(vals[2] + decimal)
			break
		end
	end
	
	return tbl_concat(charbytes)
end

function p.buildTable(frame)
	local args = getArgs(frame)
	
	local min = tonumber(args.min)
	local max = tonumber(args.max)
	
	if not min or not max then
		error('Параметры `mix` и `max` должны быть числами')
	end
	
	if min >= max then
		error('Параметр `max` должен быть больше `min`')
	end
	
	if min < 0 then -- max в любом случае больше `min`
		error('Параметр `max` и `min` должны быть положительными числами')
	end
	
	local output = {}
	
	local wiktionary = txt_trim(args.wiktionary or ''):lower()
	
	local yes = {
		["yes"] = true, ["y"] = true, ["true"] = true, ["t"] = true, ["1"] = true,
		["да"] = true, ["д"] = true, ["истина"] = true, ["и"] = true,
	}
	
	if yes[wiktionary] then
		wiktionary = true
	else
		wiktionary = false
	end
	
	local subst = txt_trim(args.subst or ''):lower()
	
	if yes[subst] then
		subst = true
	else
		subst = false
	end
	
	local unicodeChart = '[http://www.unicode.org/charts/PDF/U' .. string.format("%03x", min):upper() 
		.. '0.pdf Официальная таблица символов Консорциума Юникода]' .. (subst and '{{ref-pdf}}' or frame:expandTemplate{ title = 'ref-pdf' })
	local page = args.page
	local title = args.title
	
	local class = args.class or 'wikitable'
	local style = args.style or args.css or 'border-collapse: collapse; background: #FFFFFF;'
		.. 'font-size:large; text-align: center;'
	
	local ref_lable = (subst and '{{ref label|Version|1|^}}' or frame:expandTemplate{ title = 'ref label', args = { 'Version', '1', '^' } })
	local note_label = (subst and ':1.{{note label|Version|1|^}}По состоянию на версию{{твю}}.' or (':1.' .. frame:expandTemplate{ title = 'note label', args = { 'Version', '1', '^' } }
		.. 'По состоянию на версию ' .. frame:expandTemplate{ title = 'твю' } .. '.'))
	
	local _reserved = args.reserved
	local reserved = {} -- hash-map
	
	if _reserved then
		ref_lable = ref_lable .. (subst and '{{ref label|Reserved|2|^}}' or frame:expandTemplate{ title = 'ref label', args = { 'Reserved', '2', '^' } })
		note_label = note_label .. (subst and '\n:2.{{note label|Reserved|2|^}}Серые клетки обозначают зарезервированные кодовые позиции.'
			or '\n:2.' .. frame:expandTemplate{ title = 'note label', args = { 'Reserved', '2', '^' } }
			.. 'Серые клетки обозначают зарезервированные кодовые позиции.')
		for _, v in ipairs(txt_split(_reserved, ',', true)) do
			local ind = txt_trim(v)
			if ind ~= '' and tonumber(ind) then
				reserved[tonumber(ind)] = true
			end
		end
	end
	
	local header = "'''[[" .. page .. '|' .. (title or page) .. "]]'''"
		.. ref_lable .. '<br/>' .. unicodeChart
	
	output[#output + 1] = '{| class="' .. class .. '" style="' .. style .. '"'
	output[#output + 1] = '| colspan="17" style="background: #F8F8F8; font-size: small;" |' .. header
	output[#output + 1] = '|- style="background: #F8F8F8; font-size: small"'
	output[#output + 1] = '| style="width: 45pt" | &nbsp;'
	
	local hexNumbers = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }
	
	for _, num in ipairs(hexNumbers) do
		output[#output + 1] = '| style="width:20pt" | ' .. num
	end
	
	local font_family = args["font-family"]
	
	for ind = min, max do
		if font_family then
			output[#output + 1] = '|- style="font-family: ' .. font_family
		else
			output[#output + 1] = '|-'
		end
		
		output[#output + 1] = '| style="background: #F8F8F8; font-size: small; height: 22.5pt" | U+' 
			.. string.format("%03x", ind):upper() .. 'x'
		
		for _, num in ipairs(hexNumbers) do
			local charHex = string.format("%03x", ind):upper() .. num
			local charNum = tonumber('0x' .. charHex)
			if reserved[charNum] then
				output[#output + 1] = '| title="Reserved" style="background-color: #CCCCCC;" |'
			else
				output[#output + 1] = '| title="U+' .. charHex .. ': ' .. ((unicodeData(charHex) or {})[1] or '') .. '"| '
					.. (wiktionary and ('[[wikt:' .. utf8(charNum) .. '|' .. utf8(charNum) .. ']]') or utf8(charNum))
			end
		end
	end
	
	output[#output + 1] = '|-'
	output[#output + 1] = "| colspan=\"17\" style=\"background: #F8F8F8; font-size: small; text-align: left\" | '''Примечания'''"
	output[#output + 1] = note_label
	output[#output + 1] = '|}'
	
	return tbl_concat(output, '\n')
end

function p.test()
	mw.log(utf8(96))
	-- mw.log(string.format("%03x", 60):upper())
end

return p