Модуль:inflection-docs

Материал из Викисловаря
Документация
-- Inflection-docs v0.8.3
-- 2015-07-06

-- This parameter `lang` should be changed when move source code to another wiktionary
-- It is used to get translations from corresponding i18n-module
local lang = 'ru'

local export = {}

local u = require("Module:utils")
local wu = require("Module:wiki-utils")
local du = require("Module:inflection-docs-utils")
local i18n = mw.loadData("Module:inflection-docs/i18n/" .. lang)

local data
local affixes
local conditions
local classes
local var_changes
local affixes_changes
local args_values
local condition_affixes
local condition_flags

-- TODO: move this function to inflection-docs-utils
local function classes_order(t, key_1, key_2)
	if key_1 == nil then
		return true
	elseif key_2 == nil then
		return false
	end
	local order_class_names = du.get_ordered_classes(conditions)
	local key_1_index = 999
	local key_2_index = 999
	for i, order_class_name in pairs(order_class_names) do
		if key_1:match('^' .. order_class_name) then
			key_1_index = i
		end
		if key_2:match('^' .. order_class_name) then
			key_2_index = i
		end
	end
	if key_1_index ~= key_2_index then
		return key_1_index < key_2_index
	end
	return key_1 < key_2
end

-- Function to load corresponding data-module
local function load_data(frame)
	local data_name = frame.args['type']
	if data_name == '' then
		return 'Error in module [[Module:inflection-docs|inflection-docs]]: Name of data-module is absent.'
	end
	return mw.loadData("Module:inflection/data/" .. data_name);
end

-- Section "Template"
local function gen_template_name()
	local r = string.format('=== %s ===\n', i18n.TEMPLATE_HEADER)
	r = r .. string.format('{{%s|%s}}\n\n', i18n.TEMPLATE_TEMPLATE, data['template'])
	return r
end

-- Section "Affixes in 'affixes'"
local function gen_affixes()
	local r = ''
	r = r .. string.format('==== %s ====\n', i18n.AFFIXES_SUBHEADER:format('<code>affixes</code>'))
	r = r .. '{| ' .. wu.table_class() .. '\n'
	r = r .. string.format('! %s || %s || %s \n', i18n.NAME, i18n.VALUE, i18n.CHANGES)
	r = r .. '|-\n'
	for affix_key, affix_value in u.spairs(affixes, case_order) do
		changes = du.get_changes(affixes_changes, affix_key, wu.span_blue)
		_anchor = wu.anchor('var', affix_key) .. ' '
		r = r .. '| ' .. wu.link('var '..affix_key, wu.code_blue(affix_key)) .. '\n'
		r = r .. '| align=center | ' 
		if changes == '' then  -- we need this because if column 'Changes' is empty then anchor on empty cell is working in a wrong way.
			r = r .. _anchor
		end	
		r = r .. '"' .. wu.span_blue(affix_value) .. '"\n'
		r = r .. '|| '
		if changes then  -- if column 'Changes' is not empty then anchor should be on it.
			r = r .. _anchor
		end	
		r = r .. changes .. '\n'
		r = r .. '|-\n'
	end
	r = r .. '|}\n\n'
	return r
end

local function gen_condition_affixes()
	local r = ''
	r = r .. string.format('==== %s ====\n', i18n.AFFIXES_SUBHEADER:format('<code>conditions</code>'))
	r = r .. '{| ' .. wu.table_class() .. '\n'
	r = r .. string.format('! %s || %s \n', i18n.NAME, i18n.POSSIBLE_VALUES)
	r = r .. '|-\n'
	for affix_key, affix_value in u.spairs(condition_affixes, case_order) do
		r = r .. '| ' .. wu.link('var '..affix_key, wu.code_blue(affix_key)) .. '\n'
		r = r .. '|| ' .. wu.anchor('var', affix_key) .. ' ' .. du.get_changes(condition_affixes, affix_key, wu.span_blue) .. '\n'
		r = r .. '|-\n'
	end
	r = r .. '|}\n\n'
	return r
end

local function gen_condition_flags()
	local r = ''
	r = r .. string.format('==== %s ====\n', i18n.FLAGS_HEADER:format('<code>conditions</code>'))
	r = r .. '{| ' .. wu.table_class() .. '\n'
	r = r .. string.format('! %s || %s \n', i18n.NAME, i18n.POSSIBLE_VALUES)
	r = r .. '|-\n'
	for key, value in u.spairs(condition_flags) do
		r = r .. '| ' .. wu.link('var '..key, wu.code_blue(key)) .. '\n'
		r = r .. '|| ' .. wu.anchor('var', key) .. ' ' .. du.get_changes(condition_flags, key, wu.span_blue) .. '\n'
		r = r .. '|-\n'
	end
	r = r .. '|}\n\n'
	return r
end

local function gen_arguments()
	local r = ''
	r = r .. string.format('==== %s ====\n', i18n.ARGUMENTS_HEADER)
	r = r .. '{| ' .. wu.table_class() .. '\n'
	r = r .. string.format('! %s || %s \n', i18n.NAME, i18n.POSSIBLE_VALUES)
	r = r .. '|-\n'
	for key, value in u.spairs(args_values) do
		r = r .. '| ' .. wu.link('arg '..key, wu.code_purple(key)) .. '\n'
		r = r .. '|| ' .. wu.anchor('arg', key) .. ' ' .. du.get_changes(args_values, key, wu.span_purple) .. '\n'
		r = r .. '|-\n'
	end
	r = r .. '|}\n\n'
	return r
end

-- Section "Condtitions"
local function gen_conditions()
	-- TODO section names or groups in conditions
	local r = ''
	r = r .. string.format('==== %s ====\n', i18n.CONDITIONS_HEADER)
	r = r .. '{| ' .. wu.table_class() ..  '\n'
	r = r .. string.format('! № || %s || %s \n', i18n.CONDITIONS, i18n.ACTIONS)
	r = r .. '|-\n'
	for i, condition in pairs(conditions) do
		local if_count = 0
		local then_count = 0
		local is_section = false
		local is_subsection = false
		local has_comment = false
		local description_count = 0
		for param_name, param_value in pairs(condition) do
			if param_name == 'verdict' or param_name == 'actions' then
				then_count = then_count + 1
			elseif param_name == 'section' then
				is_section = true
				description_count = description_count + 1
			elseif param_name == 'subsection' then
				is_subsection = true
				description_count = description_count + 1
			elseif param_name == 'comment' then
				has_comment = true
				description_count = description_count + 1
			else
				if_count = if_count + 1
			end
		end
		local has_conditions = (if_count > 0 or then_count > 0)
		local has_descriptions = (description_count > 0)
		local valign = ''
		if if_count > 0 or true then  -- temp
			valign = "valign='top' "
		end
		if is_section then
			r = r .. "| colspan='3' |\n"
			r = r .. '|-\n'
		end
		local rowspan = ''
		if has_conditions and has_descriptions then
			rowspan = "rowspan='" .. (description_count + 1) .. "' "
		end
		r = r .. "| align='center' valign='top' " .. rowspan .. "| " .. wu.anchor('condition', i) .. ' ' .. wu.link('condition '..i, wu.span_green('#' .. i)) .. '\n'
		if is_section then
			r = r .. '| colspan=2 | ' .. wu.anchor('condition', i) .. ' ' .. wu.bold(i18n.SECTION .. ': ' .. wu.span_darkblue(condition['section'])) .. '\n'
			r = r .. '|-\n'
		end
		if is_subsection then
			r = r .. '| colspan=2 | ' .. wu.anchor('condition', i) .. ' ' .. wu.bold(i18n.SUBSECTION .. ': ') .. wu.span_darkblue(condition['subsection']) .. '\n'
			r = r .. '|-\n'
		end
		if has_comment then
			r = r .. '| colspan=2 | ' .. wu.anchor('condition', i) .. ' ' .. wu.italic(wu.bold(i18n.COMMENT .. ': ') .. wu.span_darkblue(condition['comment'])) .. '\n'
			r = r .. '|-\n'
		end
		if has_conditions then
			r = r .. '| ' .. valign .. '| ' .. wu.anchor('condition', i) .. ' '
			if if_count > 0 then
				r = r .. "'''" .. i18n.IF .. "'''"
				if if_count == 2 then
					r = r .. wu.span_silver(string.format(" ''(%s)''", i18n.APPLY_BOTH))
				elseif if_count > 2 then
					r = r .. wu.span_silver(string.format(" ''(%s)''", i18n.APPLY_ALL))
				end
				r = r .. '\n'
			else
				r = r .. wu.span_gray(string.format("''%s''", i18n.APPLY_ALWAYS)) .. '\n'
			end
			for param_name, param_value in pairs(condition) do
				if param_name == 'verdict' or param_name == 'actions' then
					-- skip
				elseif param_name == 'section' or param_name == 'subsection' or param_name == 'comment' then
					-- TODO
				elseif param_name == 'last' then
					r = r .. string.format('* %s: ', i18n.ENDS_WITH) .. du.join_values(param_value, wu.span_blue) .. '\n'
				elseif param_name == 'last_NOT' then
					r = r .. string.format("* %s: ", i18n.DOESNT_END_WITH) .. du.join_values(param_value, wu.span_blue) .. '\n'
				elseif param_name == 'pre_last' then
					r = r .. string.format("* %s: ", i18n.PRELAST_LETTER) .. du.join_values(param_value, wu.span_blue) .. '\n'
				elseif param_name == 'pre_last_NOT' then
					r = r .. string.format("* %s: ", i18n.PRELAST_LETTER_NOT) .. du.join_values(param_value, wu.span_blue) .. '\n'
				elseif param_name:match("^arg") ~= nil or param_name:match("^var") ~= nil then
					local NOT = false
					local var_name
					if param_name:match("_NOT$") ~= nil then
						NOT = true
						param_name = param_name:sub(1, -5)
					end
					if param_name == 'arg' or param_name == 'var' then  -- arg = {'<name>', <values>}
						var_name = param_value[1]
						param_value = param_value[2]
					else
						var_name = param_name:sub(5)  -- arg_<name> = <values>
						param_name = param_name:sub(1, 3)
					end
					if param_name == 'arg' then
						r = r .. '* ' .. i18n.SENT_ARGUMENT .. ' ' .. wu.link('arg '..var_name, wu.code_purple(var_name)) .. ' '
						if NOT then
							r = r .. i18n.ARG_NOT_EQUAL .. ' '
						else
							r = r .. i18n.ARG_EQUAL .. ' '
						end
						r = r .. du.join_values(param_value, wu.span_purple) .. '\n'
					elseif param_name == 'var' then
						r = r .. '* ' .. i18n.VAR .. ' ' .. wu.link('var '..var_name, wu.code_blue(var_name)) .. ' '
						if NOT then
							r = r .. i18n.VAR_NOT_EQUAL .. ' '
						else
							r = r .. i18n.VAR_EQUAL .. ' '
						end
						r = r .. du.join_values(param_value, wu.span_blue) .. '\n'
					end
				else
					r = r .. '* ' .. param_name .. ' = ' .. du.join_values(param_value, wu.span_blue) .. '\n'
				end
			end
			r = r .. "| valign='top' | "
			if if_count > 0 then
				r = r .. "'''" .. i18n.THEN .. "'''"
			end
			r = r .. '\n'
			if condition['actions'] then
				actions = condition['actions']
				for j, action_params in pairs(actions) do
					if action_params[1] == 'set' then
						local var_name = action_params[2]
						local var_value = du.get_action_value(action_params[3])
						r = r .. '* ' .. i18n.SET .. ' ' .. wu.link('var '..var_name, wu.code_blue(var_name)) .. ' ' .. i18n.SET_TO .. ' "' .. wu.span_blue(var_value) .. '"\n'
					else
						r = r .. "* " .. i18n.ACTION .. " '''<code style='color: purple'>" .. action_params[1] .. "</code>''' (" .. action_params[2] .. ")\n"
					end
				end
			end
			if condition['verdict'] then
				verdict = condition['verdict']
				class_name = verdict['class_name']
				r = r .. '* ' .. i18n.ADD_CLASS .. ' ' .. wu.link(i18n.CLASS..' '..class_name, wu.code_olive_bold(class_name)) .. "\n"
			end
			r = r .. '|-\n'
		end
	end
	r = r .. '|}\n\n'
	return r
end

-- Section "Classes"
local function gen_classes()
	local form_keys = du.get_form_keys(classes)
	local r = ''
	r = string.format("=== %s '''<code>classes</code>''' %s ===\n", i18n.SECTION, i18n.CLASSES_HEADER_DESC)
	for class_name, forms in u.spairs(classes, classes_order) do
		r = r .. '==== ' .. i18n.CLASS .. ' ' .. wu.code_olive_bold(class_name) .. ' ====\n'
		r = r .. '{| ' .. wu.table_class() .. '\n'
		r = r .. string.format('! %s || %s \n', i18n.FORM, i18n.VALUE)
		r = r .. '|-\n'
		for form_key, form_value in u.spairs(forms, du.case_order) do
			form_value = form_value:gsub("%>([^<]+)%<", '> + "' .. wu.span_blue('%1') .. '" + <')
			form_value = form_value:gsub("%>([^<]+)$", '> + "' .. wu.span_blue('%1') .. '"')
			form_value = form_value:gsub("%>%<", '> + <')
			form_value = form_value:gsub("%<base%>", wu.code_green('base'))
			for affix_key, affix_value in pairs(affixes) do
				form_value = form_value:gsub("%<" .. affix_key .. "%>", wu.link('var '..affix_key, wu.code_blue(affix_key)))
			end
			for affix_key, affix_value in pairs(condition_affixes) do
				form_value = form_value:gsub("%<" .. affix_key .. "%>", wu.link('var '..affix_key, wu.code_blue(affix_key)))
			end
			for key, value in pairs(condition_flags) do
				form_value = form_value:gsub("%<" .. key .. "%>", wu.link('var '..key, wu.code_blue(key)))
			end
			for i, key in pairs(form_keys) do
				form_value = form_value:gsub("%<" .. key .. "%>", wu.link('form '..key, wu.code_maroon(key)))
			end
			style = ''
			if form_value == '—' or form_value == '-' then
				style = ' align=center '
			end
			r = r .. '| ' .. wu.anchor('form', form_key) .. ' ' .. wu.link('form '..form_key, wu.code_maroon(form_key)) .. '\n'
			r = r .. '|' .. style .. '| ' .. form_value .. '\n'
			r = r .. '|-\n'
		end
		r = r .. '|}\n\n'
	end
	return r
end

local function gen_utils()
	local utils = data['utils']
	-- TODO
	return ''
end

local function gen_notes()
	local r = string.format('=== %s ===\n', i18n.NOTES_HEADER)
	r = r .. '* ' .. i18n.NOTE_SENTENCE_PART1 .. ' ' .. wu.code_blue(i18n.NOTE_VAR) .. ', ' .. wu.code_purple(i18n.NOTE_ARG) .. ', ' .. wu.span_green('#1') .. ', ' .. wu.code_maroon(i18n.NOTE_FORM)
	r = r .. i18n.NOTE_SENTENCE_PART2
	return r
end

-- Main function to call from template
function export.get(frame)
	data = load_data(frame)
	affixes = data['affixes']
	conditions = data['conditions']
	classes = data['classes']
	
	var_changes = du.gen_var_changes(conditions)
	affixes_changes = du.gen_affixes_changes(var_changes, affixes)
	local condition_vars = du.gen_condition_var_changes(var_changes, affixes)
	condition_affixes = condition_vars[1]
	condition_flags = condition_vars[2]
	args_values = du.gen_args_values(conditions)

	local result = ''
	result = result .. gen_template_name()
	result = result .. string.format('=== %s ===\n', i18n.AFFIXES_HEADER)
	result = result .. gen_affixes()
	result = result .. gen_condition_affixes()
	result = result .. "=== " .. i18n.SECTION .. " '''<code>conditions</code>''' " .. i18n.CONDITIONS_HEADER_DESC .. " ===\n"
	result = result .. gen_condition_flags()
	result = result .. gen_arguments()
	result = result .. gen_conditions()
	result = result .. gen_classes()
	result = result .. gen_utils()
	result = result .. gen_notes()

	return frame:preprocess(result)
end

return export

-- TODO: в "actions": base.replace(/k$/, 'g')
-- TODO: === Секция <code>tests</code> <span style='font-weight: normal'>''(тестовые примеры)''</span> ===
-- TODO: если секция пуста, то не отображать заголовок!!!
-- TODO: шаблон {{inflection-docs}} и автоматическое определение названия дата-модуля