Для документации этого модуля может быть создана страница Модуль:BEED/Документация

local s = {}
local roman = require('Module:RomanNumber')

--[[ Функция производит разбор строки вида <номер полутома>/<номер страницы>[/<число страниц - 1>]
     Возвращает объект с тремя полями:
        volume    - номер полутома (от 1 до 86)
        page      - номер страницы тома, на которой начинается статья
        numpages  - число страниц - 1
]]
function s.parseVolumeStr(s_data)
	if s_data == nil or s_data == '' then
		return nil
	end
	local x = mw.text.split(s_data, "/")
	if #x < 2 or #x > 3 then
		return nil
	end
    local result = {volume = tonumber(x[1]), page = tonumber(x[2]), numpages = 0}
    if #x == 3 then
    	result.numpages = tonumber(x[3])         -- число страниц - 1
    end
    return result
end

function parseStr(s_data)
    local n_volume    = nil
    local n_page      = nil
    local n_numpages  = nil
    if s_data:find("/") then
    	local x = mw.text.split(s_data, "/")
        n_volume    = tonumber(x[1])         -- номер полутома (от 1 до 86)
        n_page      = tonumber(x[2])         -- номер страницы тома, на которой начинается статья
        n_numpages  = tonumber(x[3])         -- число страниц - 1
        if n_numpages == nil then
    	    n_numpages = 0
        end
    end
    return n_volume, n_page, n_numpages
end

function s.transclude (frame, s_base, s_add, s_section, b_old_spell)
    local a = {}
    a[1] = s.parseVolumeStr(s_base)
    a[2] = s.parseVolumeStr(s_add)
    local x = {}
    for i = 1, 2 do
    	local part = a[i]
    	if part ~= nil then
            local s_part = nil
    		for n_page = part.page, part.page + part.numpages do
    			local s_template = frame:expandTemplate { title = 'ЭСБЕ/Страница', args = {part.volume, n_page, section = s_section} }
    			if s_part ~= nil and s_part ~= "" then
    				s_part = s_part .. " " .. s_template
    			else
    				s_part = s_template
    			end
            end
    		x[i] = s_part
    	else
    		x[i] = nil
        end
    end
    local result = nil
    if x[1] ~= nil and x[2] ~= nil then
        if b_old_spell then
        	result = x[1] .. '\n\n== Дополненіе. ==\n' .. x[2]
        else
        	result = x[1] .. '\n\n== Дополнение ==\n' .. x[2]
        end
    elseif x[1] ~= nil then
    	result = x[1]
    elseif x[2] ~= nil then
    	result = x[2]
    end
    return result
end

-- Возвращает номер страницы в скане по номеру полутома и страницы 
function getScanPage(n_volume, n_page)
    assert((n_volume >= 1) or (n_volume <= 86), 'Неверный номер полутома')
    assert(n_page ~= nil, 'Не задан номер страницы')
    local scans = mw.loadData ('Module:BEED/scans')
--[[ Вот этот код почему-то не работает из-за того, что next(items) не возвращает первого элемента
    local items = scans[n_volume]
    local prev_key = nil
    local prev_item = nil
    local curr_key, curr_item = next(items)  -- первый элемент коллекции 
    -- assert(curr_item ~= nil, 'пустой items')
    while curr_item ~= nil do
        mw.log('curr_item[1] = '..curr_item[1])
        mw.log('curr_item[2] = '..curr_item[2])
    	if n_page < curr_item[1] then
    		if prev_item ~= nil then
    			return prev_item[2] + n_page - prev_item[1]
    		end
    		break
    	end;
    	prev_item = curr_item
    	prev_key  = curr_key
    	curr_key, curr_item = next(items, prev_key) -- следующий элемент коллекции
    end
]]
    local prev_item = nil
    local curr_key = nil
    local curr_item = nil
    for curr_key, curr_item in ipairs(scans[n_volume]) do
    	if n_page < curr_item[1] then
    		if prev_item ~= nil then
    			return prev_item[2] + n_page - prev_item[1]
    		end
    		break
    	end;
    	prev_item = curr_item
    end    
    return nil    
end

-- Функция для вызова getScanPage()
function s.get_scan_page (frame)
    local n_volume = tonumber(frame.args[1])
    local n_page   = tonumber(frame.args[2])
    return tostring(getScanPage(n_volume, n_page))
end

-- Возвращает номер словника по имени статьи
function s.getListNum(s_name)
    local lists = mw.loadData('Module:BEED/lists')
    local prev_item = nil
    local curr_key = nil
    local curr_item = nil
    for curr_key, curr_item in ipairs(lists) do
    	if s_name < curr_item.from then
    		if prev_item ~= nil then
    			return prev_item.key
    		end
    		break
    	end;
    	prev_item = curr_item
    end    
    return nil    
end

--По номеру полутома возвращает номер тома (I, Ia, II, IIа и т. д.) и имя соотв. файла со сканом
function getVolumeData(n_volume)
    assert((n_volume >= 1) or (n_volume <= 86), 'Неверный номер полутома')
    local s_volume_name = nil
    local s_file_name = nil
    local b_even = (n_volume%2 == 0)
    if n_volume >= 83 then
    	local n_vol_num = math.floor((n_volume - 81)/2)  -- номер дополнительного тома (1 или 2)
    	s_volume_name = "доп. "..roman.toRomanNumber(n_vol_num)
    	s_file_name = "Encyclopedicheskii slovar dopoln tom "..tostring(n_vol_num)
    	if b_even then
    		s_volume_name = s_volume_name.."a"
    		s_file_name = s_file_name.." a"
        end
        s_file_name = s_file_name..".djvu"
    else
    	local n_vol_num = math.floor((n_volume + 1)/2)  -- номер основного тома (от 1 до 41)
    	s_volume_name = roman.toRomanNumber(n_vol_num)
    	s_file_name = "Encyclopedicheskii slovar tom "..tostring(n_vol_num)
    	if b_even then
    		s_volume_name = s_volume_name.."a"
    		s_file_name = s_file_name.." a"
        end
        s_file_name = s_file_name..".djvu"
    end
    return s_volume_name, s_file_name
end

-- Функция по номеру полутома возврашающая его имя
-- Используется в Шаблон:ЭСБЕ/Номер тома
function s.get_volume_name(frame)
    local n_volume = tonumber(frame.args[1])
    local s_volume_name, s_file_name = getVolumeData(n_volume)
    return s_volume_name
end

-- Функция по номеру полутома возврашающая имя файла со сканом
-- Используется в Шаблон:ЭСБЕ/Скан2
function s.get_file_name(frame)
    local n_volume = tonumber(frame.args[1])
    local s_volume_name, s_file_name = getVolumeData(n_volume)
    return s_file_name
end

function s.djvu(data)
	local n_volume, n_page, n_numpages = parseStr(data)
    local s_volume_name, s_file_name = getVolumeData(n_volume)
    return s_file_name
end

-- Функция по номеру полутома и номеру страницы возврашающая страницу файла со сканом
-- Используется в Шаблон:ЭСБЕ/Страница
function s.get_file_name_page(frame)
    local n_volume = tonumber(frame.args[1])
    local n_page = tonumber(frame.args[2])
    local s_volume_name, s_file_name = getVolumeData(n_volume)
    local n_scan_page = getScanPage(n_volume, n_page)
    return s_file_name.."/"..tostring(n_scan_page)
end
--[[
function s.djvupage (frame, data)
    local a = data or frame.args[1]
    local page, _, djvu = getScanData (a)
    return djvu .. '/' .. page
end
]]

-- Функция, реализующая Шаблон:ЭСБЕ/Скан и Шаблон:ЭСБЕ/Статья
function s.scan (frame)
	local n_volume, n_page, n_numpages
	if string.find ( frame.args[1], "%d+/%d+" ) then
		-- формат вызова: один параметр вида <номер полутома>/<номер страницы>/<число страниц - 1> (как в Шаблон:ЭСБЕ/Статья)
		n_volume, n_page, n_numpages = parseStr(frame.args[1])
	else
		-- формат вызова: три параметра (как в Шаблон:ЭСБЕ/Скан)
		n_volume = tonumber(frame.args[1])
		n_page = tonumber(frame.args[2])
		n_numpages = tonumber(frame.args[3])
	end
    if n_volume == nil or n_volume < 1 or n_volume > 86 then return end
    if n_page == nil then return end
    if n_numpages == nil then
        n_numpages  = 0
    end
    -- Определяем страницу в скане
    local n_scan_page = getScanPage(n_volume, n_page)
    -- Определяем имя файла со сканами
    local s_volume_name, s_file_name = getVolumeData(n_volume)
    local s_suffix = ""
    if n_page > 1000 then
    	n_page = n_page - 1000
    	s_suffix = " (Россия)"
    end
    local s_pages
    if n_numpages > 0 then
    	s_pages = tostring(n_page).."—"..tostring(n_page + n_numpages)..s_suffix
    else
    	s_pages = tostring(n_page)..s_suffix
    end
    if n_scan_page == nil then
    	return "<span class='plainlinks' style='white-space:nowrap'>''"..s_volume_name..", "..s_pages.."''</span>"
    else
    	return "<span class='plainlinks' style='white-space:nowrap'>[[File:Fictional page.svg|x16px|link=]] ["..frame:callParserFunction{ name = 'fullurl', args = {'File:'..s_file_name, page = tostring(n_scan_page)}}.." ''"..s_volume_name..", "..s_pages.."'']</span>"
	end
end

-- Для шаблона ЭСБЕ/Дубль с поддержкой новых словников
function s.duplicate( frame )
	--local pagename = mw.title.getCurrentTitle().text
	--local PRS = pagename:match( "/ДО$" ) or ""
	local args = mw.getCurrentFrame():getParent().args
	local articles, links, tlinks = {}, {}, {}
	local function roman(n) return require("Модуль:Math").rn(n) end
	local function link(a,v,p) return '* [[ЭСБЕ/'..a..'|'..a..']] ('..v..', '..p..')' end
	local function tlink(a,v,p) return '* {{ЭСБЕ/Ссылка|'..a..'|'..a..'}} ('..v..', '..p..')' end
	local function articles_add (a) 
		for _, s in ipairs(a) do 
			s = mw.text.trim(s)
			if s and s ~= '' and not s:find('^%d') then table.insert(articles, s) end 
		end 
	end
	articles_add ({ args['статья'], args['статья1'], args['статья2'] })
	if #articles == 0 then articles_add ({ args[1], args[2], args[3] }) end
	if #articles == 0 then return end
	local esbe = mw.loadData( 'Модуль:Отексте/ЭСБЕ' )
	local root, list, list2 = esbe.main.listroot.default, esbe.main.listnum, esbe.supplement.listnum
	for i, name in ipairs ( articles ) do
		-- нормализация названия статьи для поиска
		local sname = mw.ustring.gsub ( name, '[Йй]', 'и~' )
		sname = mw.ustring.toNFD ( sname )
		sname = mw.ustring.gsub ( mw.ustring.gsub ( sname, "[^%w,(~]", "" ), "^%p", "" )
		sname = mw.ustring.gsub ( sname, 'и~', 'й' )
		local lang = mw.language.new (mw.language.getContentLanguage().code)
		sname = lang:ucfirst ( lang:lc (sname) )
		mw.logObject(sname,'sname')
		-- отбор словников
		local toc = { t = {} }
		function toc:add(tab)
			for i, _ in ipairs ( tab ) do
				if sname >= tab[i][1] and ( tab[i+1] == nil or sname < tab[i+1][1] ) then
					for _, number in ipairs ( tab[i][2] ) do table.insert ( self.t, number ) end
					break
				end
			end
		end
		toc:add(list)
		toc:add(list2)
		mw.logObject(toc.t,'toc.t')
		--if #toc.t == 0 then return end
		-- поиск в словниках
		for _, item in ipairs(toc.t) do
			local title = mw.title.new( root .. item )
			if title then 
				text = title:getContent()
				if text then 
					local pat = "{{%s*Статья в словнике%s*|%s*" .. escapePattern( name ) .. "%s*|[^|{}]+|%s*([^|{}]+)[^{}]*}}"
					local foundstr = mw.ustring.match( text, pat )
					if foundstr then
						foundstr = foundstr:gsub( "^(%d+).*$", "%1" )
						local prefix = ''
						local vol = item:gsub( "^(%d+).*$", "%1" ); vol = tonumber(vol)
						if vol > 82 then vol = vol - 82; prefix = 'доп. ' end
						local rvol = prefix..roman((math.floor(vol+1)/2)); if vol % 2 == 0 then rvol = rvol .. 'a' end
						links[i] = link( name, rvol, foundstr )
						tlinks[i] = tlink( name, rvol, foundstr )
						break 
					end
				end
			end
		end
		-- если не найдено
		links[i] = links[i] or '* {{ЭСБЕ/Ссылка|' .. name .. '|' .. name .. '}}'
		tlinks[i] = tlinks[i] or links[i]
	end
	local result = table.concat(links,'\n')
	if #tlinks > 0 then result = result .. frame:preprocess('\n{{Версии|\n' .. table.concat(tlinks,'\n') ..'\n|title=Другие версии}}') end
	if #result > 0 then result = result .. '[[Категория:ЭСБЕ:Дубли статей]]' end
	return result
end

-- escape certain characters in regex patterns
function escapePattern( pattern_str )
	return mw.ustring.gsub( pattern_str, "([%(%)%.%%%+%-%*%?%[%^%$%]])", "%%%1" );
end

return s