Module:grc-decl/table

Definition from Wiktionary, the free dictionary
Jump to: navigation, search
Text-x-generic with pencil.svg This module needs documentation.
Please document this module by describing its purpose and usage on the documentation page.

local path = 'Module:grc-decl'
local headers = mw.loadData(path .. '/decl/classes').headers

local str_find = string.find
local str_match = string.match
local str_gsub = string.gsub
local ustring_gsub = mw.ustring.gsub

local export = {}

--[[
	Basic letters with and without diacritics, plus digamma and combining
	diacritics.
]]
local word_characters = mw.loadData('Module:grc-utilities/data').word_characters

-- displayed in cell that has no form in it
local empty_cell = '—'

local nonAttic_note = 'Dialects other than Attic are not well attested. ' ..
	'Some forms may be based on conjecture. Use with caution.'
local Attic_note = 'This table gives Attic inflectional endings. ' ..
	'For declension in other dialects, see [[Appendix:Ancient Greek dialectal declension]].'

local form_redirects = {
	AS = 'NS', VS = 'NS',
	DD = 'GD', AD = 'ND', VD = 'ND',
	AP = 'NP', VP = 'NP',
}

local case_names = { 'Nominative', 'Genitive', 'Dative', 'Accusative', 'Vocative' }

local function make_repl(mapping)
	return function(key) return mapping[key] end
end

local lang = require('Module:languages').getByCode('grc')

local full_link = require('Module:links').full_link
local function entry_link(term, alt)
	return full_link({ lang = lang, term = term, alt = alt, tr = '-' }, nil, false)
end

-- Creates a callable table that saves previous transliterations.
-- Helpful because most paradigms have some syncretic forms; particularly useful for neuter forms.
local transliterate = require('Module:fun').memoize(require('Module:grc-translit').tr)

local tag_translit = require('Module:script utilities').tag_translit
local function format_translit(Greek_text)
	return tag_translit(transliterate(Greek_text), lang, 'default', 'style="color: #888;"')
end

local m_labels = mw.loadData('Module:grc:Dialects')
local get_label = require('Module:alternative forms').getLabel
local function get_dialect_label(dialect)
	return get_label(dialect, m_labels)
end

local function make_number_table(number_arg)
	local numbers = {}
	for _, number in ipairs{ { 'S', 'Singular' }, { 'D', 'Dual' }, { 'P', 'Plural' } } do
		if number_arg[number[1]] then
			table.insert(numbers, number[2])
		end
	end
	return numbers
end

local function link(alt)
	if alt == '-' then
		return '-'
	end
	
	alt = str_gsub(alt, 'σ$', 'ς') --just in case
	if str_find(alt, '%b()') then
		-- for example, πᾶσῐ(ν) → πᾶσῐν, πᾶσῐ
		local with_paren_content, no_paren_content =
				str_gsub(alt, '[%(%)]', ''),
				str_gsub(alt, '%b()', '')
		return str_gsub(alt,
			'(%(?)([^%(%)]+)(%)?)',
			function(open_paren, content, close_paren)
				if open_paren == '(' and close_paren == ')' then
					return open_paren ..
							entry_link(with_paren_content, content) ..
							close_paren
				else
					return entry_link(no_paren_content, content)
				end
			end)
	else
		return entry_link(alt)
	end
end

-- Memoization
local _link = link
local link_cache = {}
link = function(alt)
	local ret = link_cache[alt]
	if not ret then
		ret = _link(alt)
		link_cache[alt] = ret
	end
	return ret
end

local function get_header(code, irregular, indeclinable)
	if not code then
		if irregular then
			return 'Irregular declension'
		elseif indeclinable then
			return 'Declension'
		else
			return "??"
		end
	elseif #code > 5 then
		mw.log('grc-decl/header not code')
		return code
	else
		return headers[code] or
			error('No header for the code ' .. code .. '.')
	end
end

local function link_form(args, f, istitle)
	local t = args.adjective and args.atable or args.ctable
	local q
	
	-- grab the form
	if args[f] then
		q = args[f]
		require('Module:debug').track('grc-decl/form-override')
	elseif f == 'NDP' then
		q = args.form_cache['MDP']
	elseif t[f] then
		q = t[f]
	elseif form_redirects[f] then
		q = args.form_cache[form_redirects[f]]
	elseif form_redirects[f:sub(2)] then
		q = args.form_cache[f:sub(1, 1) .. form_redirects[f:sub(2)]]
	elseif f:match('N[^N].') then
		q = args.form_cache['M' .. f:sub(2)]
	end
	
	if q == nil or q == '' or q == '-' or q == '—' then return empty_cell end
	
	-- if it is a title form, strip all but the first variation
	if istitle then
		q = str_match(q, '[^,/]+') --capture up to comma or slash (needs standardization)
		q = ustring_gsub(q, '%s+$', '') --strip final whitespace
	else
		-- convert commas to slashes and space the slashes for legibility
		q = ustring_gsub(q, '%s*[/,]%s*', ' / ')
		args.form_cache[f] = q
	end
	
	-- concat article
	if (not args.adjective) and args.article[f] and f:sub(1, 1) ~= 'V' then
		q = args.article[f] .. ' ' .. q
	end
	
	--[[
		An Ancient Greek word character optionally preceded by a hyphen or
		followed by a sequence of word characters or parentheses.
		Matches -ᾰ́ς, σοῖσι(ν), as well as cases with parentheses in the middle
		of the word.
	]]
	local x, _ = ustring_gsub(q, '%-?[' .. word_characters .. '][' .. word_characters .. '()]*', link)
	
	if istitle then
		return x
	else
		return x .. '<br/>' .. format_translit(q)
	end
end

local function make_title(args)
	local q = {}
	table.insert(q, get_header(args.declheader, args.irregular, args.indeclinable) .. ' of ')
	for _, number_code in ipairs{ 'S', 'D', 'P' } do
		if args.number[number_code] then
			table.insert(q, link_form(args, 'N' .. number_code, true) .. '; ' .. link_form(args, 'G' .. number_code, true))
			break
		end
	end
	
	if args.dial then
		table.insert(args.titleapp, get_dialect_label(args.dial))
	end
	
	if args.titleapp[1] then
		table.insert(q, ' (' .. table.concat(args.titleapp, ', ') .. ')')
	end
	
	return table.concat(q)
end

local case_header =
[[
|-
! style="background:#C0C0C0; font-style:italic;" | {{{case_name}}}
]]
local form_cell =
[[
| style="background:#F5F5F5;" | {{{form}}}
]]
local function make_rows(args, nums)
	local s = {}
	args.form_cache = {}
	for _, case_name in ipairs(case_names) do
		table.insert(s, (case_header:gsub("{{{case_name}}}", case_name)))
		local case_abbr = case_name:sub(1, 1)
		for _, number in ipairs(nums) do
			table.insert(s, (form_cell:gsub('{{{form}}}', link_form(args, case_abbr .. number:sub(1, 1)))))
		end
	end
	return table.concat(s)
end

local notes_template =
[[
|-
! style="background:#C0C0C0; font-style:italic;" | Notes:
| {{{extra_cell}}}style="background:#F5F5F5; text-align:left; font-size:90%;" colspan="13" | <span class="use-with-mention">{{{notes}}}</span>
]]

local function make_notes(args)
	table.insert(args.notes, 1,
		args.dial ~= 'att' and nonAttic_note
		or Attic_note)
	
	if args.user_notes then -- add user notes
		table.insert(args.notes, args.user_notes)
	end
	
	if args.debug then
		table.insert(args.notes, args.debug)
	end
	
	local notes = notes_template
		:gsub('{{{extra_cell}}}', args.adjective and '\n| ' or '')
		:gsub('{{{notes}}}', table.concat(args.notes, '\n'))
	
	return notes
end

local number_header =
[[
! style="background:#C0C0C0; font-style:italic;{{{width}}}" | {{{number}}}
]]

local noun_table_top =
[=[
<div class="NavFrame" style="clear:both; width:{{{width1}}};">
<div class="NavHead" style="text-align: center">{{{title}}}</div>
<div class="NavContent">
{| style="width:100%; background:#A9A9A9; color:#000000; text-align:center;" class="inflection-table inflection-table-grc" cellspacing="1"
! style="background:#B0C4DE; font-style:italic; width:{{{width2}}};" | Case / #
]=]

function export.make_table(args)
	local nums = make_number_table(args.number)
	-- This can be simplified.
	local s = {
		(noun_table_top
			:gsub('{{{width1}}}', args.number.F and '100%' or '44%')
			:gsub('{{{title}}}', make_title(args))
			:gsub('{{{width2}}}', args.number.F and '16%' or '37%'))
	}
	local width = args.number.F and ' width:28%;' or ''
	for _, j in ipairs(nums) do
		table.insert(s, (number_header:gsub('{{{width}}}', width):gsub('{{{number}}}', j)))
	end
	table.insert(s, make_rows(args, nums))
	table.insert(s, make_notes(args))
	table.insert(s, '|}</div></div>')
	if args.categories[1] and mw.title.getCurrentTitle().nsText == '' then
		table.insert(s, require('Module:utilities').format_categories(args.categories, lang))
	end
	return table.concat(s)
end

local function make_title_adj(args, genders)
	if args.title then
		return args.title
	else
		local q = {}
		table.insert(q, get_header(args.adeclheader, args.irregular, args.indeclinable) .. ' of ')
		
		for _, number in ipairs{ 'S', 'D', 'P' } do
			if args.number[number] then
				local gender_forms = {}
				for _, gender in ipairs(genders) do
					table.insert(gender_forms, link_form(args, gender .. 'N' .. number, true))
				end
				table.insert(q, table.concat(gender_forms, '; '))
				break
			end
		end
		
		if args.dial then
			table.insert(args.titleapp, get_dialect_label(args.dial))
		end
		
		if args.titleapp[1] then
			table.insert(q, ' (' .. table.concat(args.titleapp, ', ') .. ')')
		end
		
		return table.concat(q)
	end
end

local function make_rows_adj(args, nums, genders)
	local s = {}
	args.form_cache = {}
	for _, case_name in ipairs(case_names) do
		table.insert(s, (case_header:gsub('{{{case_name}}}', case_name)))
		for _, number in ipairs(nums) do
			table.insert(s, '|\n')
			local case_number = case_name:sub(1, 1) .. number:sub(1, 1)
			for _, gender in ipairs(genders) do
				table.insert(s, (form_cell:gsub('{{{form}}}', link_form(args, gender .. case_number))))
			end
		end
	end
	return table.concat(s)
end

local function make_acs_adj(args, nums)
	-- this should only apply to pronouns. I think.
	if #nums < 3 then return '' end
	
	-- get the adverb (we couldn't do this before)
	args.atable.adv = args.atable.adv or
			str_gsub(str_gsub(args.atable['MGP'], 'ν$', 'ς'), 'ν<', 'ς<')
	args.atable.comp = args.atable.comp
	args.atable.super = args.atable.super
	
	local fill = {
		colspan = args.act and args.act[2] and '3' or '2',
		adv = link_form(args, 'adv'),
		comp = link_form(args, 'comp'),
		super = link_form(args, 'super'),
	}
	
	local s = [=[|-
! style="background:#B0C4DE;" rowspan="2" | ''Derived forms''
|
| style="background:#C0C0C0;" colspan={{{colspan}}} | ''Adverb''
|
| style="background:#C0C0C0;" colspan={{{colspan}}} | ''Comparative''
|
| style="background:#C0C0C0;" colspan={{{colspan}}} | ''Superlative''
|-
|
| style="background:#F5F5F5;" colspan={{{colspan}}} | {{{adv}}}
|
| style="background:#F5F5F5;" colspan={{{colspan}}} | {{{comp}}}
|
| style="background:#F5F5F5;" colspan={{{colspan}}} | {{{super}}}
]=]
	s = str_gsub(s, '{{{([a-z0-9_]+)}}}', make_repl(fill))
	return s
end

local adj_table_top =
[=[
<div class="NavFrame" style="clear:both; width:100%;">
<div class="NavHead" style="text-align:center;">{{{title}}}</div>
<div class="NavContent">
{| style="width:100%; background:#A9A9A9; color:#000000; text-align:center;" class="inflection-table inflection-table-grc" cellspacing="1"
! style="background:#CECECE;" | ''Number''
]=]

function export.make_table_adj(args)
	local nums = make_number_table(args.number)
	
	local threept = not(args.act and args.act[2] == nil)
	local genders = threept and { 'M', 'F', 'N' } or { 'M', 'N' }
	local number_header =
[=[
! style="width:0.5%" |
! style="background:#CECECE; width:28%" colspan=]=] .. (threept and '3' or '2') .. "| ''{{{number}}}''\n"

	local gender_headers =
[=[
|
! style="background:#C0C0C0;" | ''Masculine]=] .. (threept and [=[''
! style="background:#C0C0C0;" | '']=] or [=[ / ]=]) .. [=[Feminine''
! style="background:#C0C0C0;" | ''Neuter''
]=]

	local s = {
		(adj_table_top:gsub('{{{title}}}', make_title_adj(args, genders)))
	}
	for _, number in ipairs(nums) do
		table.insert(s, (number_header:gsub('{{{number}}}', number)))
	end
	table.insert(s, [=[|-
! style="background:#B0C4DE;" | ''Case/Gender''
]=])
	for _, _ in ipairs(nums) do
		table.insert(s, gender_headers)
	end
	table.insert(s, make_rows_adj(args, nums, genders))
	table.insert(s, make_acs_adj(args, nums))
	table.insert(s, make_notes(args))
	table.insert(s, '|}</div></div>')
	
	if args.categories[1] and mw.title.getCurrentTitle().nsText == '' then
		table.insert(s, require('Module:utilities').format_categories(args.categories, lang))
	end
	
	return table.concat(s)
end

return export