Module:Descendants tree

local export = {}

local function track(page) --Special:WhatLinksHere/Template:tracking/descendants tree/PAGE return require("Module:debug").track("descendants tree/" .. page) end

local function language_header_error(entry_name, language_name) mw.log("No header for " .. language_name .. " was found in the entry "			.. entry_name .. ".") track("language header not found") end

local function get_content_after_senseid(content, entry_name, lang, id) local m_templateparser = require("Module:templateparser") local code = lang:getCode local t_start = nil local t_end = nil for name, args, _, index in m_templateparser.findTemplates(content) do		if name == "senseid" and args[1] == code and args[2] == id then t_start = index elseif name == "etymid" then if args[1] == code and args[2] == id then t_start = index elseif t_start ~= nil and t_end == nil then t_end = index end elseif name == "head" and args[1] == code then if args["id"] == id then t_start = index elseif args["id"] ~= nil and t_start ~= nil and t_end == nil then t_end = index end end end if t_start == nil then error("Could not find the correct senseid template in the entry "		.. entry_name .. " (with language " .. code .. " and id '" .. id .. "')")	end if t_end == nil then -- terminate on L2 or another "Etymology ..." header -- match L2 and remove it and everything after it		content = string.gsub(content:sub(t_start), "\n==[^=].+$", "") -- match Etymology header and remove it and everything after it		content = string.gsub(content, "\n===+%s*Etymology.+$", "") return content end return content:sub(t_start, t_end) end

function export.getAlternativeForms(lang, term, id) local entry_name = require("Module:links").getLinkPage(term, lang) local page = mw.title.new(entry_name) local content = page:getContent if not content then -- FIXME, should be an error track("alts-nonexistent-page") return "" end local _, index = string.find(content,		"==[ \t]*" .. require("Module:string").pattern_escape(lang:getCanonicalName) .. "[ \t]*==") if not index then language_header_error(entry_name, lang:getCanonicalName) -- FIXME, should be an error track("alts-lang-not-found") return "" end

if id then content = get_content_after_senseid(content, entry_name, lang, id) index = 0 end local _, next_lang = string.find(content, "\n==[^=\n]+==", index, false) local _, index = string.find(content, "\n(====?=?)[ \t]*Alternative forms[ \t]*%1", index, false) local langCodeRegex = require("Module:string").pattern_escape(lang:getCode) index = string.find(content, "{{alte?r?|" .. langCodeRegex .. "|[^|}]+", index) if (not index) or (next_lang and next_lang < index) then -- FIXME, should be an error track("alts-alter-not-found") return "" end local next_section = string.find(content, "\n(=+)[^=]+%1", index) local alternative_forms_section = string.sub(content, index, next_section) local parameters_list -- This assumes that there are no nested templates in. for alternative_form_template_parameters in string.gmatch(alternative_forms_section,		"") do		local double_pipe_pos = string.find(alternative_form_template_parameters, "||", 1, true) local term_parameters = string.sub(alternative_form_template_parameters, 1,			double_pipe_pos and double_pipe_pos - 1) if term_parameters then parameters_list = mw.text.split(term_parameters, "|") break end end if not parameters_list or #parameters_list == 0 then -- FIXME, should be an error track("alts-alter-no-params") return "" end local terms_list = {} local items = { t = {}, id = {}, alt = {}, tr = {}, ts = {}, g = {} }	for _, parameter in ipairs(parameters_list) do		local parameterName, value = string.match(parameter, "^([^=]+)=(.+)$") if parameterName and value then local item_type, index = string.match(parameterName, "(%D+)(%d)") if item_type and index and items[item_type] then items[item_type][tonumber(index)] = value elseif parameterName == "sc" then items.sc = value elseif items[parameterName] then items[parameterName][1] = value end else table.insert(terms_list, parameter) end end local i = 1 while true do		local term = terms_list[i] local alt, tr, ts, g, t = items.alt[i], items.tr[i], items.ts[i], items.g[i], items.t[i] if not (term or alt or tr or ts or g or t) then break end local sc		if items.sc then sc = require "Module:scripts".getByCode(items.sc) end terms_list[i] = require("Module:links").full_link({			term = term,			lang = lang,			sc = sc,			alt = alt,			tr = tr,			ts = ts,			genders = g,			gloss = t		}, nil, true) i = i + 1 end return ", " .. table.concat(terms_list, ", ") end

function export.getDescendants(lang, term, id, noerror) local entry_name = require("Module:links").getLinkPage(term, lang) local page = mw.title.new(entry_name) local content = page:getContent local namespace = mw.title.getCurrentTitle.nsText if not content then -- FIXME, should be an error track("desctree-nonexistent-page") return "" end -- Ignore columns and blank lines content = string.gsub(content, "%s", "") content = string.gsub(content, "%s", "") content = string.gsub(content, "%s", "") content = string.gsub(content, "\n?{{(desc?%-%l+)|?[^}]*}}",		function (template_name)			if template_name == "desc-top" or template_name == "desc-bottom" or template_name == "des-top" or template_name == "des-mid" or template_name == "des-bottom" then				return ""			end		end) content = string.gsub(content, "\n%s*\n", "\n") local _, index = string.find(content,		"%f[^\n%z]==[ \t]*" .. lang:getCanonicalName .. "[ \t]*==", nil, true) if not index then _, index = string.find(content, "%f[^\n%z]==[ \t]*"				.. require("Module:utilities").pattern_escape(lang:getCanonicalName)				.. "[ \t]*==", nil, false) end if not index then language_header_error(entry_name, lang:getCanonicalName) -- FIXME, should be an error track("desctree-lang-not-found") return "" end

if id then content = get_content_after_senseid(content, entry_name, lang, id) index = 0 end

local _, next_lang = string.find(content, "\n==[^=\n]+==", index, false) local _, index = string.find(content, "\n(====*)[ \t]*Descendants[ \t]*%1", index, false) if not index then if noerror and (namespace == "" or namespace == "Reconstruction") then track("desctree-no-descendants") return "(" ..				"Please either change this template to " ..				"or insert a ====Descendants==== section in " ..				entry_name .. ") " .. ""		else error("No Descendants section was found in the entry " .. entry_name .. ".") end elseif next_lang and next_lang < index then if noerror and (namespace == "" or namespace == "Reconstruction") then track("desctree-no-descendants-in-lang-section") return "(" ..				"Please either change this template to " ..				"or insert a ====Descendants==== section in " ..				entry_name .. ") " .. ""		else error("No Descendants section was found in the entry " .. entry_name					.. " under the header for " .. lang:getCanonicalName .. ".") end end -- Skip past final equals sign. index = index + 1 -- Skip past spaces or tabs or HTML comments. while true do		local new_index = string.match(content, "^[ \t]+", index) or string.match(content, "^<!%-%-.-%-%->", index) if not new_index then break end index = new_index end local items = require("Module:array") local frame = mw.getCurrentFrame local previous_list_markers = "" -- Skip paragraphs at beginning of Descendants section. while true do		local new_index = content:match("^\n[^%*:=][^\n]*", index) if not new_index then break else index = new_index end end previous_index = 1 -- Find a consecutive series of list items that begins directly after the -- Descendants header. -- start_index and previous_index are used to check that list items are -- consecutive. for start_index, list_markers, item, index in string.gmatch(content:sub(index), "\n([%*:]+) *([^\n]+)") do		if start_index ~= previous_index then break end -- Preprocess, but replace recursive calls to avoid template loop errors item = string.gsub(item, "{{desctree|", "{{#invoke:etymology/templates/descendant|descendants_tree|") item = frame:preprocess(item) local difference = #list_markers - #previous_list_markers if difference > 0 then for i = #previous_list_markers + 1, #list_markers do				items:insert(list_markers:sub(i, i) == "*" and "" or "") end else if difference < 0 then for i = #previous_list_markers, #list_markers + 1, -1 do					items:insert(previous_list_markers:sub(i, i) == "*" and "" or "") end else items:insert(previous_list_markers:sub(-1, -1) == "*" and "" or "") end if previous_list_markers:sub(#list_markers, #list_markers) ~= list_markers:sub(-1, -1) then items:insert(list_markers:sub(-1, -1) == "*" and "" or "") end end items:insert(list_markers:sub(-1, -1) == "*" and "" or "") items:insert(item) previous_list_markers = list_markers previous_index = index end for i = #previous_list_markers, 1, -1 do		items:insert(previous_list_markers:sub(i, i) == "*" and "</li></ul>" or "</dd></dl>") end return items:concat end

return export