模組:Fro-verb
This module provides helper functions for Old French verb inflection tables.
The external entry point is show, meant for calling from templates, and there is a debugging entry point show2 meant for calling from the debug console, which takes arguments for the current and parent frame and constructs mock-up frames.
Most arguments are meant to be passed in through the parent frame, i.e. arguments specified to the template that invokes this module, rather than to the invocation itself. The invocation arguments are
- type: The only mandatory argument. Group the verb's conjugation belongs to ("i", "ii" or "iii").
- ier: Same as the parent-frame 'ier' argument. Used by
{{fro-conj-ier}}
. - supe: Same as the parent-frame 'supe' argument. Used by
{{fro-conj-er-e}}
and{{fro-conj-ier-e}}
.
The parent frame arguments are documented thoroughly in {{fro-conj-iii}}
.
The following are the primary templates invoking this module. One of them should be used to create the conjugation table of a verb.
{{fro-conj-er}}
: For group-I (-er) verbs.{{fro-conj-ier}}
: For group-Ia (-ier) verbs.{{fro-conj-ii}}
: For group-II (-ir with -iss- infix) verbs.{{fro-conj-iii}}
: For group-III verbs. These have various infinitive forms, generally either -ir (without -iss- infix), -oir, -eir, or -re.{{fro-conj-iii-ii}}
: For group-III-II verbs. These are group-III -ir verbs that have additional, alternative forms with group II endings.
--[[
Author: User:benwing
Todo:
1. Possibly, make it possible to specify multiple stems to stem parameters
separated by commas. This is easiest for fut= but gets trickier for all the
rest because there are multiple parameters per stem.
2. Should write a routine that extracts the present stems (pres, press, ier,
plus equivalent for 2 .. 9, and handles the special-casing for 'pres/steme'
and 'ier', and checks to make sure a multipart spec wasn't given, and uses
the corresponding prese if so), and uses them in handle_imperfect() and
handle_pres_part(). Also need to handle 'prese' in the subjunctive and
imperative code, which should probably be rewritten along the lines of
handle_imperfect().
3. Create separate categories for verbs ending in various consonants, e.g.
g, c, ch, ill, gn, etc.
4. Figure out how to create a category for verbs that are missing their
conjugation (but are still identified as verbs because of {{head|fro|verb}},
which puts them into Category:Old French verbs). Possibly this isn't
possible except using a bot.
5. Intro to Old French by Kibler claims that -lm requires a supporting vowel.
Possibly -ln as well. What about -lf? Evidently -ld does not require a
supporting vowel, due to 'chaud' < 'calidus'. Should we try to handle -lm
specially? We might want to also handle -aum, -eum (but not -oum as in
'noumer', where 'ou' represents earlier 'o' rather than an 'l'). These
verbs are rare enough, though, and hard enough to handle correctly that
perhaps we should just require explicitly specifying supe= (or equivalently,
using {{temp|fro-conj-er-e}} etc.).
--]]
local m_links = require("Module:links")
local m_utilities = require("Module:utilities")
local lang = require("Module:languages").getByCode("fro")
local export = {}
-- Functions that do the actual inflecting by creating the forms of a basic term.
local inflections = {}
local rfind = mw.ustring.find
local rsub = mw.ustring.gsub
local rmatch = mw.ustring.match
local usub = mw.ustring.sub
local function ine(x) -- If Not Empty
if x == nil then
return nil
elseif rfind(x, '^".*"$') then
local ret = rmatch(x, '^"(.*)"$')
return ret
elseif rfind(x, "^'.*'$") then
local ret = rmatch(x, "^'(.*)'$")
return ret
elseif x == "" then
return nil
else
return x
end
end
local function contains(tab, item)
for _, value in pairs(tab) do
if value == item then
return true
end
end
return false
end
local function insert_if_not(tab, item)
if not contains(tab, item) then
table.insert(tab, item)
end
end
----------------------------------------------------------------------------
-- Functions for working with stems
-- Given the stem as it appears before e/i, generate the corresponding
-- stem for a/o.
function steme_to_stema(steme)
if rfind(steme, "c$") then
-- Need to assign to a var because rsub returns 2 values, and
-- assigning to one var fetches only the first one
local stema = rsub(steme, "c$", "ç")
return stema
elseif rfind(steme, "g$") then
local stema = rsub(steme, "g$", "j")
return stema
else
return steme
end
end
-- Given the stem as it appears before a/o, generate the corresponding
-- stem for e/i.
function stema_to_steme(stema)
if rfind(stema, "c$") then
-- Need to assign to a var because rsub returns 2 values, and
-- assigning to one var fetches only the first one
local steme = rsub(stema, "c$", "qu")
return steme
elseif rfind(stema, "g$") then
local steme = rsub(stema, "g$", "gu")
return steme
elseif rfind(stema, "ç$") then
local steme = rsub(stema, "ç$", "c")
return steme
else
return stema
end
end
-- Given the stem as it appears before a/o, generates the corresponding
-- stem for u.
function stema_to_stemu(stema)
if rfind(stema, "qu$") then
-- Need to assign to a var because rsub returns 2 values, and
-- assigning to one var fetches only the first one
local stemu = rsub(stema, "qu$", "c")
return stemu
elseif rfind(stema, "gu$") then
local stemu = rsub(stema, "gu$", "g")
return stemu
else
return stema
end
end
-- Given the stem as it appears before e/i, generates the corresponding
-- stem for u.
function steme_to_stemu(steme)
return stema_to_stemu(steme_to_stema(steme))
end
-- Get the stem from the infinitive. Return stem and whether stem is
-- soft-vowel (true except for -re and -oir verbs).
--
-- Currently we rely on the IER argument being set in order to remove
-- -ier from an infinitive. An alternative is to always check for -ier
-- and assume that cases like 'signifier' which is an '-er' verb with a
-- stem 'signifi' are handled by explicitly specifying the stem. (We
-- assume this anyway in the case of any '-ir' verb whose stem ends
-- in '-o', such as 'oir' "to hear".)
local function get_stem_from_inf(inf, ier, ir)
local nsub = 0
-- if 'ier' arg given and stem ends in -ier, strip it off.
if ier then
stem, nsub = rsub(inf, "ier$", "")
if nsub > 0 then
return stem, "ier", true
end
end
-- Check for -er, -oir, -eir, -ir, -ïr, -re in sequence.
-- Must check for -oir, -eir before -ir.
stem, nsub = rsub(inf, "er$", "")
if nsub > 0 then
return stem, "er", true
end
if not ir then
stem, nsub = rsub(inf, "oir$", "")
if nsub > 0 then
return stem, "oir", false
end
stem, nsub = rsub(inf, "eir$", "")
if nsub > 0 then
return stem, "eir", true
end
end
stem, nsub = rsub(inf, "ir$", "")
if nsub > 0 then
return stem, "ir", true
end
stem, nsub = rsub(inf, "ïr$", "")
if nsub > 0 then
return stem, "ir", true
end
stem, nsub = rsub(inf, "re$", "")
if nsub > 0 then
return stem, "re", false
end
error("Unrecognized infinitive '" .. inf .. "'")
end
-- Get the present stem, either explicitly passed in as the first arg or
-- implicitly through the page name, assumed to be the same as the
-- infinitive. Currently we rely on the 'ier' argument being set in
-- order to remove -ier from an infinitive. An alternative is to
-- always check for -ier an assume that cases like 'signifier' which
-- is an '-er' verb with a stem 'signifi' are handled by explicitly
-- specifying the stem. (We assume this anyway in the case of any '-ir'
-- verb whose stem ends in '-o', such as 'oir' "to hear".)
local function get_stem_from_frame(frame)
local stem = ine(frame.args[1])
-- if stem passed in and non-blank, use it.
if stem then
return stem
end
local inf = mw.title.getCurrentTitle().text
local ier = ine(frame.args["ier"])
local ir = ine(frame.args["ir"])
local stem, ending, is_soft = get_stem_from_inf(inf, ier, ir)
return stem
end
-- External entry point for get_stem_from_frame(). Optional stem is first
-- argument, optional 'ier' argument is used for -ier verbs (see
-- get_stem_from_frame()).
function export.get_stem(frame)
return get_stem_from_frame(frame)
end
-- Joins a stem to an ending, automatically handling the stem changes needed
-- before different vowels (steme/stema/stemu), and automatically adding umlauts
-- to the stem and/or ending when necessary. The stem form passed in is steme
-- (the form before e/i).
function join(stem, ending)
local firstend = usub(ending, 1, 1)
-- change the stem as appropriate
if firstend == "a" or firstend == "o" then
stem = steme_to_stema(stem)
elseif firstend == "u" then
stem = steme_to_stemu(stem)
end
-- add an umlaut to i or u at beginning of ending if there's a vowel
-- at the end of the stem, unless it's the same vowel, or a diphthong
-- ending in i/u, or gu/qu.
if firstend == "i" and
(rfind(stem, "[aeoü]$") or
rfind(stem, "u$") and not rfind(stem, "[gqaäeëéiïoö]u$")) then
ending = rsub(ending, "^i", "ï")
elseif firstend == "u" and
(rfind(stem, "[aeïo]$") or
rfind(stem, "i$") and not rfind(stem, "[aäeëéoöuü]i$")) then
ending = rsub(ending, "^u", "ü")
-- add an umlaut to i or u at end of stem if ending begins with e/é and
-- stem doesn't end in diphthong, or gu/qu.
elseif firstend == "e" or firstend == "é" then
if rfind(stem, "i$") and not rfind(stem, "[aäeëéoöuü]i$") then
stem = rsub(stem, "i$", "ï")
elseif rfind(stem, "u$") and not rfind(stem, "[gqaäeëéiïoö]u$") then
stem = rsub(stem, "u$", "ü")
end
end
return stem .. ending
end
----------------------------------------------------------------------------
-- Functions for generating arrays of endings
-- Generate an ending array (five-element, see below), where a supporting ''e''
-- is needed.
local function supporting_e(extra)
local text = "In the present tense an extra supporting ''e'' is needed in the first-person singular indicative and throughout the singular subjunctive, and the third-person singular subjunctive ending ''-t'' is lost. "
text = text .. extra
return {"", "e", "es", "e", text}
end
-- Generate a general ending array (five-element, see below).
local function mod3(ending, zero, s, t)
local text = nil
if ending == zero then
text = string.format("原本會規則變化成 *''-%ss''、*''-%st'' 的詞形被修改成以 ''%s''、''%s'' 結尾。",
ending, ending, s, t)
else
text = string.format("原本會規則變化成 *''-%s''、*''-%ss''、*''-%st'' 的詞形被修改成以 ''%s''、''%s''、''%s'' 結尾。",
ending, ending, ending, zero, s, t)
end
return {ending, zero, s, t, text}
end
-- Return the stem and an array of five elements. In the array, the first
-- is the ending to be substitute, the second is the add_zero ending to
-- substitute, the third is the add_s ending to substitute, the fourth is
-- the add_t ending to substitute and the fifth is the text going into the
-- comment at the top of the conj table. IER should be specified for -ier
-- verbs, which only occur with palatal(ized) final consonants, and causes
-- a different interpretation of certain consonants, esp. final l.
-- If SUPE is specified, force a supporting -e to be added (normally this
-- is inferred automatically from the stem). If OLD is specified, don't
-- convert l before consonant to u.
local function get_endings(stem, ier, supe, old)
local ret = {"", "", "s", "t", ""}
local ending = nil
-- canonicalize ngn to gn when after vowel or r, l
if rfind(stem, "[aeiouyäëïöüÿrl]ngn$") then
ending = "ngn"
stem = rsub(stem, "ngn$", "gn")
end
-- canonicalize double letter to single after vowel, except for l
-- we need to treat Cill and Cil differently
if rfind(stem, "[aeéiouyäëïöüÿ]([bcdfghjkmnpqrstvwxz])%1$") then
ending = rmatch(stem, "(..)$")
stem = rsub(stem, "([bcdfghjkmnpqrstvwxz])%1$", "%1")
end
if supe then
ret = supporting_e("")
elseif rfind(stem, "mb$") then
ret = mod3("mb", "mp", "ns", "nt")
elseif rfind(stem, "mp$") then
ret = mod3("mp", "mp", "ns", "nt")
elseif rfind(stem, "([aeéiouyäëïöüÿrlmn])[bpdtḍṭfvczk]$") then
local lastchar = rmatch(stem, "(.)$")
ending = ending or lastchar
if lastchar == "b" or lastchar == "p" then
ret = mod3(ending, "p", "s", "t")
elseif lastchar == "d" or lastchar == "t" then
ret = mod3(ending, "t", "z", "t")
elseif lastchar == "ḍ" or lastchar == "ṭ" then
ret = mod3(ending, "ṭ", "z", "t")
elseif lastchar == "f" or lastchar == "v" then
ret = mod3(ending, "f", "s", "t")
elseif lastchar == "c" then
ret = mod3(ending, "z", "z", "zt")
ret[5] = ret[5] .. "In addition, ''c'' becomes ''ç'' before an ''a, o'' or ''u'' to keep the /ts/ sound intact. "
-- at least in bauptizer, 'z' appears to represent /z/, like 's'
-- in the same position
elseif lastchar == "z" then
ret = mod3(ending, "s", "s", "st")
elseif lastchar == "k" then
ret = mod3(ending, "k", "s", "t")
else
error("Logic error! Unhandled case for character '" .. lastchar .. "'")
end
elseif rfind(stem, "([aeéiouyäëïöüÿrlmn])c?qu$") then
ending = rmatch(stem, "(c?qu)$")
ret = mod3(ending, "c", "s", "t")
elseif rfind(stem, "([aeéiouyäëïöüÿrlmn])g?gu$") then
ending = rmatch(stem, "(g?gu)$")
ret = mod3(ending, "c", "s", "t")
elseif rfind(stem, "([aeéiouyäëïöüÿrlmn])ct$") then
-- convicter, paincter. Best guess here
ret = mod3("ct", "ct", "cz", "ct")
elseif rfind(stem, "([aeéiouyäëïöüÿrlmn])s[bpdtczk]$") then
local lastchar = rmatch(stem, "(.)$")
ending = "s" .. (ending or lastchar)
if lastchar == "b" or lastchar == "p" then
-- Best guess here, esp. for 'sb'
ret = mod3(ending, "sp", "s", "st")
elseif lastchar == "d" or lastchar == "t" then
-- croster, etc.
-- brosder. Best guess here
ret = mod3(ending, "st", "z", "st")
elseif lastchar == "c" or lastchar == "z" then
-- drescier, laiszier(?). Best guess here
ret = mod3(ending, "z", "z", "zt")
if lastchar == "c" then
ret[5] = ret[5] .. "In addition, ''c'' becomes ''ç'' before an ''a, o'' or ''u'' to keep the /ts/ sound intact. "
end
elseif lastchar == "k" then
ret = mod3(ending, "sk", "s", "st")
else
error("Logic error! Unhandled case for character '" .. lastchar .. "'")
end
elseif rfind(stem, "([aeéiouyäëïöüÿrlmn])s[qg]u$") then
-- Best guess here
ending = rmatch(stem, "(s[qg]u)$")
ret = mod3(ending, "sc", "s", "st")
elseif old and (rfind(stem, "([aeéouäëöü])[iy]ll?$") or (rfind(stem, "[aeéouäëöü]ll?$") and ier)) then
ending = rmatch(stem, "(.[iy]?ll?)$")
local prev = rmatch(stem, "(.[iy]?)ll?$")
ret = mod3(ending, prev .. "l", prev .. "lz", prev .. "lt")
elseif rfind(stem, "([aeéoäëö])[iy]ll?$") or (rfind(stem, "[aeéoäëö]ll?$") and ier) then
ending = rmatch(stem, "(.[iy]?ll?)$")
local i = ine(rmatch(stem, "([iy]?)ll?$")) or "i"
local prev = rmatch(stem, "(.)[iy]?ll?$")
ret = mod3(ending, prev .. i .. "l", prev .. "uz", prev .. "ut")
elseif rfind(stem, "[uü][iy]ll?$") or (rfind(stem, "[uü]ll?$") and ier) then
ending = rmatch(stem, "([uü][iy]?ll?)$")
local i = ine(rmatch(stem, "([iy]?)ll?$")) or "i"
local u = rmatch(stem, "([uü])[iy]?ll?$")
ret = mod3(ending, u .. i .. "l", u .. "z", u .. "t")
elseif rfind(stem, "[iïyÿ]ll$") or (rfind(stem, "[iïyÿ]l$") and ier) then
ending = rmatch(stem, "([iïyÿ]ll?)$")
local i = rmatch(stem, "([iïyÿ])ll?$")
ret = old and mod3(ending, i .. "l", i .. "lz", i .. "lt")
or mod3(ending, i .. "l", i .. "z", i .. "t")
elseif rfind(stem, "([^iu])[eé]ll?$") or rfind(stem, "ëll?$") then
-- first rfind() above should not have ïü or ë in it
ending = rmatch(stem, "([eéë]ll?)$")
local e = rmatch(stem, "([eéë])ll?$")
-- FIXME: Should this be eals, ealt when old?
ret = old and mod3(ending, e .. "l", e .. "ls", e .. "lt")
or mod3(ending, e .. "l", e .. "aus", e .. "aut")
elseif rfind(stem, "([aeéoäö])ll?$") then -- not ë, handled above
-- first rmatch below should not have ïü or ë in it
ending = rmatch(stem, "([iu][eé]ll?)$") or rmatch(stem, "([aoäö]ll?)$")
local prev = rmatch(stem, "([iu][eé])ll?$") or rmatch(stem, "([aoäö])ll?$")
ret = old and mod3(ending, prev .. "l", prev .. "ls", prev .. "lt")
or mod3(ending, prev .. "l", prev .. "us", prev .. "ut")
elseif rfind(stem, "([iuyïüÿ])ll?$") then
ending = rmatch(stem, "(.ll?)$")
local prev = rmatch(stem, "(.)ll?$")
ret = old and mod3(ending, prev .. "l", prev .. "ls", prev .. "lt")
or mod3(ending, prev .. "l", prev .. "s", prev .. "t")
elseif rfind(stem, "[iyïÿl]gn$") then
local prev = rmatch(stem, "([iyïÿl])gn$")
ret = mod3(prev .. (ending or "gn"), prev .. "ng", prev .. "nz", prev .. "nt")
elseif rfind(stem, "rgn$") then
ret = mod3("r" .. (ending or "gn"), "rng", "rz", "rt")
elseif rfind(stem, "([aeéouäëöü])gn$") then
local prev = rmatch(stem, "(.)gn$")
ret = mod3(prev .. (ending or "gn"), prev .. "ing", prev .. "inz", prev .. "int")
elseif rfind(stem, "rm$") then
ret = mod3("rm", "rm", "rs", "rt")
elseif rfind(stem, "rn$") then
ret = mod3("rn", "rn", "rz", "rt")
elseif rfind(stem, "[aeéiouyäëïöüÿl]m$") then
ret = mod3(ending or "m", "m", "ns", "nt")
elseif rfind(stem, "s$") then
ret = mod3(ending or "s", "s", "s", "st")
elseif rfind(stem, "g$") then
ret = supporting_e("In addition, ''g'' becomes ''j'' before an ''a'' or an ''o'' to keep the /dʒ/ sound intact. ")
elseif rfind(stem, "j$") or rfind(stem, "x$")
or rfind(stem, "[^aeéiouyäëïöüÿ][bcdfghjklmnpqrtvwxz]$") then
ret = supporting_e("")
elseif ending then
-- doubled rr, nn, hh
local lastchar = rmatch(stem, "(.)$")
ret = mod3(ending, lastchar, lastchar .. "s", lastchar .. "t")
end
return ret
end
----------------------------------------------------------------------------
-- Functions for handling particular sorts of endings
-- Convert a stressed verb stem to the form used with a zero ending
-- (1st sing pres indic, also, 1st sing pres subj of -er verbs).
-- See get_endings() for meaning of IER and SUPE.
function add_zero(stem, ier, supe, old)
local e = get_endings(stem, ier, supe, old)
-- We need to assign to a variable here because rsub() returns multiple
-- values and we want only the first returned. Return rsub() directly
-- and all values get returned and appended to the string.
local ret, nsub = rsub(stem, e[1] .. "$", e[2])
assert(nsub == 1)
return ret
end
-- Convert a stressed verb stem to the form used with a -s ending
-- (2nd sing pres indic of -ir/-oir/-re verbs, 2nd sing pres subj of
-- -er verbs). Same code could be used to add -s to nouns except that
-- handling of -c stems needs to be different (need to treat as hard /k/
-- not /ts/). See get_endings() for meaning of IER, SUPE and OLD.
function add_s(stem, ier, supe, old)
local e = get_endings(stem, ier, supe, old)
-- We need to assign to a variable here because rsub() returns multiple
-- values and we want only the first returned. Return rsub() directly
-- and all values get returned and appended to the string.
local ret, nsub = rsub(stem, e[1] .. "$", e[3])
assert(nsub == 1)
return ret
end
-- Convert a stressed verb stem to the form used with a -t ending
-- (3rd sing pres indic of -ir/-oir/-re verbs, 3rd sing pres subj of
-- -er verbs). See get_endings() for meaning of IER, SUPE and OLD.
function add_t(stem, ier, supe, old)
local e = get_endings(stem, ier, supe, old)
-- We need to assign to a variable here because rsub() returns multiple
-- values and we want only the first returned. Return rsub() directly
-- and all values get returned and appended to the string.
local ret, nsub = rsub(stem, e[1] .. "$", e[4])
assert(nsub == 1)
return ret
end
-- Add -r, for the group-iii future
function add_r(stem, old)
local ret = stem .. "r"
if rfind(stem, "ss$") then
ret = rsub(stem, "ss$", "str")
elseif rfind(stem, "is$") then
ret = rsub(stem, "is$", "ir")
elseif rfind(stem, "ïs$") then
ret = rsub(stem, "ïs$", "ïr")
elseif rfind(stem, "s$") then
ret = stem .. "dr"
elseif rfind(stem, "g?n$") then
ret = rsub(stem, "g?n$", "ndr")
elseif rfind(stem, "m$") then
ret = stem .. "br"
elseif rfind(stem, "i?ll?$") then
ret = old and rsub(stem, "ll?$", "ldr") or rsub(stem, "i?ll?$", "udr")
elseif rfind(stem, "qu$") then
ret = rsub(stem, "qu$", "cr")
elseif rfind(stem, "gu$") then
ret = rsub(stem, "gu$", "gr")
elseif rfind(stem, "[^aeiouäëïöü]r$") then
ret = stem .. "er"
end
return ret
end
----------------------------------------------------------------------------
-- Functions for handling comments at the top of conjugation tables
-- Return true if this is an irregular verb, due to stems or particular
-- forms being explicitly specified
function irreg_verb(args, skip)
if skip == nil then skip = {}
elseif type(skip) == "string" then skip = {skip}
end
for k,v in pairs(args) do
if k == "pres" and rfind(v, "/") or
k ~= "pres" and k ~= "prese" and k ~= "presa" and
k ~= "ier" and k ~= "supe" and k ~= "aux" and k ~= "refl" and
k ~= "repl" and k ~= "prefix" and k ~= "suffix" and
k ~= "old" and k ~= "ei" and k ~= "ir" and
k ~= "impers" and k ~= "inf" and k ~= "comment" and
not contains(skip, k) and v ~= '' then
return true
end
end
return false
end
-- Return comment describing phonetic changes to the verb in the present
-- tense. Appears near the top of the conjugation chart. STEM is the stressed
-- stem. See get_endings() for meaning of IER, SUPE and OLD.
function phonetic_verb_comment(stem, ier, supe, old)
local e = get_endings(stem, ier, supe, old)
return e[5]
end
-- Return comment describing phonetic and stem changes to the verb in the
-- present tense. Appears near the top of the conjugation chart. STEME is
-- the unstressed stem before e/i, STEMS the stressed stem.
-- See get_endings() for meaning of IER, SUPE and OLD.
function verb_comment(args, group, steme, stems, ier, supe, old)
if args["comment"] then
return args["comment"] .. " "
end
local com = group == "i" and phonetic_verb_comment(stems, ier, supe, old) or ""
local irreg = irreg_verb(args, 'press')
if steme ~= stems then
if args["repl"] then
-- If there's a search/replace, it might change the stressed or
-- unstressed stems. FIXME: apply search/replace to the stems
-- themselves.
com = com .. "本動詞有一個不同的重讀現在時詞幹"
else
com = com .. "本動詞有一個重讀現在時詞幹 ''" ..
(args["prefix"] or "") .. stems .. (args["suffix"] or "") ..
"'',與非重讀的詞幹 ''" ..
(args["prefix"] or "") .. steme .. (args["suffix"] or "") .. "'' "
end
com = com ..
(irreg and "及其他不規則形式有別。" or "有別。")
elseif irreg then
com = com .. "本動詞的變位不規則。"
end
return com
end
----------------------------------------------------------------------------
-- Main inflection-handling functions
-- Main entry point
function export.show(frame)
local origargs = frame:getParent().args
local args = {}
-- Convert empty arguments to nil, and "" or '' arguments to empty
for k, v in pairs(origargs) do
args[k] = ine(v)
end
-- Create the forms
local data = {forms = {}, categories = {}, group = {}, comment = "",
refl = args["refl"],
impers = args["impers"],
ier = args["ier"] or ine(frame.args["ier"]),
supe = args["supe"] or ine(frame.args["supe"])
}
data.forms.infinitive =
{args["inf"] or mw.title.getCurrentTitle().text}
data.inf_from_title = not args["inf"]
data.inf_no_affix = data.forms.infinitive[1]
if args["prefix"] and data.inf_from_title then
data.inf_no_affix = rsub(data.inf_no_affix, "^" .. args["prefix"], "")
end
if args["suffix"] and data.inf_from_title then
data.inf_no_affix = rsub(data.inf_no_affix, args["suffix"] .. "$", "")
end
-- Set the soft vowel ('pres') and hard vowel ('presa') present stems.
-- They can be explicitly set using 'pres' and 'presa' params.
-- If one is set, the other is inferred from it. If neither is set,
-- both are inferred from the infinitive.
data.prese = args["pres"] and not rfind(args["pres"], "/") and args["pres"] or
args["prese"]
local inf_stem, inf_ending, inf_is_soft =
get_stem_from_inf(data.inf_no_affix, data.ier, args["ir"])
if not data.prese then
if inf_is_soft then
data.prese = inf_stem
else
data.prese = stema_to_steme(inf_stem)
end
end
data.old = args["old"]
data.ei = args["ei"] or data.old or inf_ending == "eir"
data.forms.aux = {data.refl and "estre" or args["aux"] or
data.ei and "aveir" or "avoir"}
-- Find what type of verb is it (hard-coded in the template).
-- Generate standard conjugated forms for each type of verb.
local infl_type = ine(frame.args["type"])
if not infl_type then
error("Verb type ('type' arg) not specified.")
elseif inflections[infl_type] then
inflections[infl_type](args, data)
else
error("Verb type '" .. infl_type .. "' not supported.")
end
for _, group in ipairs(data.group) do
table.insert(data.categories, "古法語第" .. group .. "組動詞")
end
table.insert(data.categories, "以-" .. inf_ending .. "結尾的古法語動詞")
if data.impers then
table.insert(data.categories, "古法語非人称動詞")
end
-- Get overridden forms
process_overrides(args, data)
-- Add a prefix if specified
if args["prefix"] then
add_affix(data, args["prefix"], true)
end
-- Add a suffix if specified
if args["suffix"] then
add_affix(data, args["suffix"], false)
end
-- Apply any replacements
local repl = args["repl"]
if repl then
replace_all(data, repl)
end
-- Add links
add_links(data)
-- Add reflexive pronouns
if data.refl then
add_reflexive_pronouns(args, data)
end
return make_table(data) .. m_utilities.format_categories(data.categories, lang)
end
-- Version of main entry point meant for calling from the debug console.
function export.show2(args, parargs)
local frame = {args = args, getParent = function() return {args = parargs} end}
return export.show(frame)
end
-- If ARGBASE == "foo", return an array of
-- {{args["foo"]},{args["foo2"]},...,{args["foo9"]}}.
-- If ARGBASE is a sequence of strings, return an array of sequences, e.g.
-- if ARGBASE == {"foo", "bar"}, return an array of
-- {{{args["foo"]},{args["bar"]}},{{args["foo2"]},{args["bar2"]}},...,{{args["foo9"]},{args["bar9"]}}}
-- There is an extra level of {}'s because nil can't be inserted into an array
function get_args(args, argbase)
if type(argbase) == "string" then
local theargs = {{args[argbase]}}
for j = 2, 9 do
table.insert(theargs, {args[argbase .. j]})
end
return theargs
else
local theargs = {}
local onearg = {}
for i, item in ipairs(argbase) do
table.insert(onearg, {args[item]})
end
table.insert(theargs, onearg)
for j = 2, 9 do
onearg = {}
for i, item in ipairs(argbase) do
table.insert(onearg, {args[item .. j]})
end
table.insert(theargs, onearg)
end
return theargs
end
end
-- Replaces terms with overridden ones that are given as additional named parameters.
function process_overrides(args, data)
-- Each term in current is overridden by one in overN, if it exists.
local function override(current, over)
current = current or {}
local ret = {}
local i = 1
local function getover(n)
return args[over .. n]
end
-- Insert an override entry, possibly splitting on commas and
-- inserting multiple forms.
local function insert_override(entry)
if entry ~= "-" then
if rfind(entry, ",") then
local forms = mw.text.split(entry, ",")
for _, form in ipairs(forms) do
table.insert(ret, form)
end
else
table.insert(ret, entry)
end
end
end
-- Look for overrides at the beginning
if getover(0) then
insert_override(getover(0))
end
-- Look for override of all current forms
if getover("") then
current = {}
insert_override(getover(""))
end
-- See if any of the existing items in current have an override specified.
while current[i] do
if getover(i) then
insert_if_not(data.categories, "Old French verbs with partial overrides")
insert_override(getover(i))
else
table.insert(ret, current[i])
end
i = i + 1
end
-- We've reached the end of current.
-- Look in the override list to see if there are any extra forms to
-- add on to the end. NOTE: We've deprecated this and made it an error.
-- Use ...n to insert forms at the end.
while i <= 9 do
if getover(i) then
error("Attempt to partially override a non-existent form: arg "
.. over .. i .. "=" .. getover(i) ..
": use " .. over .. "n=" .. getover(i) .. " instead.")
-- insert_override(getover(i))
end
i = i + 1
end
-- Look for overrides at the end
if getover("n") then
insert_override(getover("n"))
end
return ret
end
-- Mark terms with any additional parameters as irregular, except for
-- certain ones that we consider normal variants.
if irreg_verb(args) then
table.insert(data.categories, "古法語不規則動詞")
end
--[[
This function replaces former code like this:
data.forms.pret_indc_1sg = override(data.forms.pret_indc_1sg, "pret1s")
data.forms.pret_indc_2sg = override(data.forms.pret_indc_2sg, "pret2s")
data.forms.pret_indc_3sg = override(data.forms.pret_indc_3sg, "pret3s")
data.forms.pret_indc_1pl = override(data.forms.pret_indc_1pl, "pret1p")
data.forms.pret_indc_2pl = override(data.forms.pret_indc_2pl, "pret2p")
data.forms.pret_indc_3pl = override(data.forms.pret_indc_3pl, "pret3p")
--]]
local function handle_tense_override(tense, short)
local pnums = {"1sg", "2sg", "3sg", "1pl", "2pl", "3pl"}
local pnums_short = {"1s", "2s", "3s", "1p", "2p", "3p"}
for pn = 1, #pnums do
local pnum = pnums[pn]
local pnum_short = pnums_short[pn]
local tensepnum = tense .. "_" .. pnum
data.forms[tensepnum] =
override(data.forms[tensepnum], short .. pnum_short)
end
end
-- Non-finite forms
-- data.forms.infinitive = override(data.forms.infinitive, "inf")
data.forms.pres_ptc = override(data.forms.pres_ptc, "presp")
data.forms.past_ptc = override(data.forms.past_ptc, "pastp")
handle_tense_override("pres_indc", "pres") -- Present
handle_tense_override("impf_indc", "imperf") -- Imperfect
handle_tense_override("pret_indc", "pret") -- Preterite
handle_tense_override("futr_indc", "fut") -- Future
handle_tense_override("cond", "cond") -- Conditional
handle_tense_override("pres_subj", "sub") -- Present subjunctive
handle_tense_override("impf_subj", "impsub") -- Imperfect subjunctive
-- Imperative
data.forms.impr_2sg = override(data.forms.impr_2sg, "imp2s")
data.forms.impr_1pl = override(data.forms.impr_1pl, "imp1p")
data.forms.impr_2pl = override(data.forms.impr_2pl, "imp2p")
end
-- Adds reflexive pronouns to the appropriate forms
function add_reflexive_pronouns(args, data)
-- Gather pronoun parameters
local cprons = {}
local vprons = {}
cprons["1sg"] = args["me"] or "me "
cprons["2sg"] = args["te"] or "te "
cprons["3sg"] = args["se"] or "se "
cprons["1pl"] = args["nos"] or "nos "
cprons["2pl"] = args["vos"] or "vos "
cprons["3pl"] = cprons["3sg"]
vprons["1sg"] = args["me"] or "m'"
vprons["2sg"] = args["te"] or "t'"
vprons["3sg"] = args["se"] or "s'"
vprons["1pl"] = args["nos"] or "nos "
vprons["2pl"] = args["vos"] or "vos "
vprons["3pl"] = vprons["3sg"]
function add_refl(person, form)
-- FIXME! YUCK! This hard-codes knowledge of how the links are formatted.
-- Perhaps we should go back to the old way of having the reflexive code
-- also insert links.
if rfind(form, "^<span.*>[^a-zA-Z]*[aeiouAEIOU]") then
return vprons[person] .. form
else
return cprons[person] .. form
end
end
-- Go over all the forms in the list
for key, subforms in pairs(data.forms) do
-- Extract the person/number from the last 3 characters of the key
local person = key:sub(-3)
-- Skip these three, as they already had pronouns added earlier
if cprons[person] and key ~= "impr_2sg" and key ~= "impr_1pl" and key ~= "impr_2pl" then
-- Go through each of the alternative subforms and add the pronoun
for key2, subform in ipairs(subforms) do
data.forms[key][key2] = add_refl(person, subform)
end
end
end
-- Handle infinitive
for key, subform in ipairs(data.forms.infinitive) do
data.forms.infinitive[key] = add_refl("3sg", subform)
end
-- Handle imperatives
for key, subform in ipairs(data.forms.impr_2sg) do
data.forms.impr_2sg[key] = subform .. (data.ei and "-tei" or "-toi")
end
for key, subform in ipairs(data.forms.impr_1pl) do
data.forms.impr_1pl[key] = subform .. "-nos"
end
for key, subform in ipairs(data.forms.impr_2pl) do
data.forms.impr_2pl[key] = subform .. "-vos"
end
end
-- Add links to all forms
function add_links(data)
for key, subforms in pairs(data.forms) do
for key2, subform in ipairs(subforms) do
data.forms[key][key2] = make_link(subform)
end
end
end
-- Add a prefix or suffix to all forms. AFFIX is what to add; IS_PREFIX is
-- true if it's a prefix, otherwise a suffix.
function add_affix(data, affix, is_prefix)
for key, subforms in pairs(data.forms) do
-- Don't add affix to aux, nor infinitive
-- that comes from the page title.
if key ~= "aux" and (key ~= "infinitive" or not data.inf_from_title) then
for key2, subform in ipairs(subforms) do
data.forms[key][key2] =
(is_prefix and affix .. subform or subform .. affix)
end
end
end
end
-- Apply all replacements
function replace_all(data, repl)
for splitrepl in mw.text.gsplit(repl, ",") do
local fromto = mw.text.split(splitrepl, "/")
if #fromto ~= 2 then
error("Replace spec '" .. splitrepl .. "' needs exactly one / in it")
end
local from = fromto[1]
local to = fromto[2]
for key, subforms in pairs(data.forms) do
-- Don't search/replace on aux, nor on infinitive
-- that comes from the page title. Don't want e.g. infinitive
-- 'offrir' to become 'offffrir'.
if key ~= "aux" and (key ~= "infinitive" or not data.inf_from_title) then
for key2, subform in ipairs(subforms) do
data.forms[key][key2] = rsub(data.forms[key][key2], from, to)
end
end
end
end
end
-- Inflection functions
-- Implementation of inflect_tense(). See that function. Also used directly
-- to add the imperative, which has only three forms.
function inflect_tense_1(data, tense, stems, endings, pnums)
-- First, initialize any nil entries to sequences.
for i, pnum in ipairs(pnums) do
if data.forms[tense .. "_" .. pnum] == nil then
data.forms[tense .. "_" .. pnum] = {}
end
end
-- Now add entries
for i = 1, #pnums do
-- Extract endings for this person-number combo
local ends = endings[i]
if type(ends) == "string" then ends = {ends} end
-- Extract stem for this person-number combo
local stem = stems
if type(stem) == "table" then stem = stem[i] end
-- Add entries for stem + endings
for j, ending in ipairs(ends) do
local form = join(stem, ending)
if ine(form) and form ~= "-" and
(not data.impers or pnums[i] == "3sg") then
table.insert(data.forms[tense .. "_" .. pnums[i]], form)
end
end
end
end
-- Add to DATA the inflections for the tense indicated by TENSE (the prefix
-- in the data.forms names, e.g. 'impf_subj'), formed by combining the STEMS
-- (either a single string or a sequence of six strings) with the
-- ENDINGS (a sequence of six values, each of which is either a string
-- or a sequence of one or more possible endings). If existing
-- inflections already exist, they will be added to, not overridden.
function inflect_tense(data, tense, stems, endings)
local pnums = {"1sg", "2sg", "3sg", "1pl", "2pl", "3pl"}
inflect_tense_1(data, tense, stems, endings, pnums)
end
-- Like inflect_tense() but for the imperative, which has only three forms
-- instead of six.
function inflect_tense_impr(data, tense, stems, endings)
local pnums = {"2sg", "1pl", "2pl"}
inflect_tense_1(data, tense, stems, endings, pnums)
end
function inflect_pres(data, tense, group, steme, stems, ier, supe)
local i = ier and "i" or ""
if steme ~= stems then
insert_if_not(data.categories, "Old French verbs with stem alternations")
end
local oldt = data.old and "ṭ" or ""
if tense == "impr" and group == "i" then
inflect_tense_impr(data, tense,
{stems, steme, steme},
{"e", "ons", i .. "ez"})
elseif tense == "impr" and group == "ii" then
inflect_tense_impr(data, tense, steme,
{"is", "issons", "issez"})
elseif tense == "impr" and group == "iii" then
inflect_tense_impr(data, tense,
{add_zero(stems, ier, supe, data.old), steme, steme},
{"", "ons", i .. "ez"})
elseif tense == "pres_indc" and group == "i" then
inflect_tense(data, tense,
{add_zero(stems, ier, supe, data.old), stems, stems, steme, steme, stems},
{"", "es", "e".. oldt, "ons", i .. "ez", "ent"})
elseif tense == "pres_indc" and group == "ii" then
inflect_tense(data, tense, steme,
{"is", "is", "ist", "issons", "issez", "issent"})
elseif tense == "pres_subj" and group == "ii" then
inflect_tense(data, tense, steme,
{"isse", "isses", "isse" .. oldt, "issons", "issez", "issent"})
elseif tense == "pres_subj" and group == "iii" then
inflect_tense(data, tense, {stems, stems, stems, steme, steme, stems},
{"e", "es", "e" .. oldt,
ier and {"iens", "ons"} or "ons", i .. "ez", "ent"})
else -- pres_indc group iii or pres_subj group i
inflect_tense(data, tense,
{add_zero(stems, ier, supe, data.old),
add_s(stems, ier, supe, data.old),
add_t(stems, ier, supe, data.old), steme, steme, stems},
{"", "", "", "ons", i .. "ez", "ent"})
end
end
-- Split a string into entries separated by slashes, each representing one
-- of the possible person/number combinations. Each entry in turn may consist
-- of one or more forms, separated by commas. NUMREQ is how many entries need
-- to be present, defaulting to 6.
function split_multipart(str, numreq)
local entries = mw.text.split(str, "/")
numreq = numreq or 6
if #entries ~= numreq then
error("Expected " .. numreq .. " entries in multipart string '" .. str .. "'")
end
for i, entry in ipairs(entries) do
if rfind(entry, ",") then
entries[i] = mw.text.split(entry, ",")
else
entries[i] = {entry}
end
end
return entries
end
function handle_pres(args, data, group, steme, stems)
local ier = data.ier
local supe = data.supe
if args["pres"] and rfind(args["pres"], "/") then
inflect_tense(data, "pres_indc", "", split_multipart(args["pres"]))
else
inflect_pres(data, "pres_indc", group, steme, stems, ier, supe)
end
-- If sub not set, derive from indicative.
-- If subs not set, derive from sub if set, else from indicative.
local sub_steme = args["sub"]
local sub_dash = sub_steme == "-"
local sub_specified = sub_steme
if sub_steme and rfind(sub_steme, "/") then
inflect_tense(data, "pres_subj", "", split_multipart(sub_steme))
elseif not sub_dash then
if not sub_specified then
sub_steme = steme
end
local sub_stems = args["subs"]
if not sub_stems then
sub_stems = sub_specified and sub_steme or stems
end
inflect_pres(data, "pres_subj", group, sub_steme, sub_stems,
args["subier"] or (not sub_specified and ier),
args["subsupe"] or (not sub_specified and supe))
end
-- Repeat exactly for the imperative.
local imp_steme = args["imp"]
local imp_dash = imp_steme == "-"
local imp_specified = imp_steme
if imp_steme and rfind(imp_steme, "/") then
inflect_tense_impr(data, "impr", "", split_multipart(imp_steme, 3))
elseif not imp_dash then
if not imp_specified then
imp_steme = steme
end
local imp_stems = args["imps"]
if not imp_stems then
imp_stems = imp_specified and imp_steme or stems
end
inflect_pres(data, "impr", group, imp_steme, imp_stems,
args["impier"] or (not imp_specified and ier),
args["impsupe"] or (not imp_specified and supe))
end
-- If indic stem specified, we add indic values, and also corresponding
-- subj values if separate subj stem not specified.
for i = 2, 9 do
steme = args["pres" .. i]
local multi_indic = steme and rfind(steme, "/")
local indic_specified = not multi_indic and steme
if multi_indic then
inflect_tense(data, "pres_indc", "", split_multipart(steme))
elseif indic_specified then
stems = args["press" .. i] or steme
ier = args["ier" .. i]
supe = args["supe" .. i]
inflect_pres(data, "pres_indc", group, steme, stems, ier, supe)
end
-- Handle subjunctive as above.
sub_steme = args["sub" .. i]
sub_dash = sub_steme == "-"
sub_specified = sub_steme
if sub_steme and rfind(sub_steme, "/") then
inflect_tense(data, "pres_subj", "", split_multipart(sub_steme))
elseif not sub_dash and (indic_specified or sub_specified) then
if not sub_specified then
sub_steme = steme
end
sub_stems = args["subs" .. i]
if not sub_stems then
sub_stems = sub_specified and sub_steme or stems
end
inflect_pres(data, "pres_subj", group, sub_steme,
sub_stems,
args["subier" .. i] or (not sub_specified and ier),
args["subsupe" .. i] or (not sub_specified and supe))
end
-- Repeat for the imperative.
imp_steme = args["imp" .. i]
imp_dash = imp_steme == "-"
imp_specified = imp_steme
if imp_steme and rfind(imp_steme, "/") then
inflect_tense_impr(data, "impr", "", split_multipart(imp_steme, 3))
elseif not imp_dash and (indic_specified or imp_specified) then
if not imp_specified then
imp_steme = steme
end
imp_stems = args["imps" .. i]
if not imp_stems then
imp_stems = imp_specified and imp_steme or stems
end
inflect_pres(data, "impr", group, imp_steme,
imp_stems,
args["impier" .. i] or (not imp_specified and ier),
args["impsupe" .. i] or (not imp_specified and supe))
end
end
end
-- Add to DATA the endings for the preterite and imperfect
-- subjunctive, with unstressed e/i stem STEME, stressed e/i stem STEMS,
-- conjugation type PTY and corresponding value of IMPSUB.
function inflect_pret_impf_subj(data, stem, pty, pret, pretu, prets, impsub)
local steme = pretu or pret or stem
local stems = prets or pretu or pret or stem
if pret and rfind(pret, "/") then
inflect_tense(data, "pret_indc", "", split_multipart(pret))
inflect_impf_subj(data, impsub, nil, nil, nil)
elseif pty then
insert_if_not(data.categories, "有" .. pty .. "過去時的古法語動詞")
-- WARNING: If the second person singular of any of these is not a
-- simple string, you will need to modify the handling below of
-- the imperfect subjunctive, which relies on this form.
local oldt = data.old and "ṭ" or ""
local all_endings =
pty == "弱變化-a" and {"ai","as","a" .. oldt,"ames","astes","erent"} or
pty == "弱變化-a2" and {"ai","as","a" .. oldt,"ames","astes","ierent"} or
pty == "弱變化-i" and {"i","is","i" .. oldt,"imes","istes","irent"} or
-- FIXME: "Grammatik des Altfranzösischen I-III" (Dr. E. Schwan and
-- Dr. D. Behrens), p. 229, claims that the old form of the weak-i2
-- ending is "ieḍrent", e.g. "rendieḍrent" < "rendęderunt", whereas the
-- early 12th-century forms of E. Einhorn "Old French: A Concise Handbook"
-- p.43 have only "ierent". I trust the latter because Schwan and Behrens
-- also have the 3rd-singular as "rendiet" with a hard -t < "rendędit",
-- but in reality it should be "rendieṭ" (i.e. "rendiéṭ") with a soft -ṭ,
-- as per Einhorn and also William Kibler "An Introduction to Old French".
-- Schwan and Behrens' forms would be correct if the underlying Vulgar
-- Latin forms are correct, but I think haplology has deleted the second
-- -d- so you should really have 3rd-singular "rendęt" > "rendiéṭ" and
-- 3rd-plural "rendęrunt" > "rendierent".
pty == "弱變化-i2" and {"i","is","ié" .. oldt,"imes","istes","ierent"} or
pty == "強變化-i" and {"","is","t","imes","istes","rent"} or
pty == "強變化-id" and {"","is","t","imes","istes",{"drent","rent"}} or
pty == "弱變化-u" and {"ui","us","u" .. oldt,"umes","ustes","urent"} or
pty == "強變化-u" and {"ui","eüs","ut","eümes","eüstes","urent"} or
pty == "強變化-o" and {"oi","eüs","ot","eümes","eüstes","orent"} or
pty == "強變化-st" and {"s","sis","st","simes","sistes","strent"} or
pty == "強變化-sd" and {"s","ṣis","st","ṣimes","ṣistes","sdrent"} or
error("Unrecognized prettype value '" .. pty .. "'")
-- Always use one of the weak stems unless we have strong-stem endings
local all_stems =
rfind(pty, "^強變化-") and {stems, steme, stems, steme, steme, stems} or
{steme, steme, steme, steme, steme, steme}
inflect_tense(data, "pret_indc", all_stems, all_endings)
inflect_impf_subj(data, impsub, steme,
join(all_stems[2], all_endings[2]), pty)
elseif impsub then
inflect_impf_subj(data, impsub, nil, nil, nil)
end
end
function inflect_impf_subj(data, impsub, steme, pret2s, pty)
-- Handle imperfect subj, which follows the same types as the preterite
-- and is built off of the 2nd person singular form, although we need to
-- special-case weak-a and weak-a2
local impsub_endings =
{"se","ses","t",{"sons","siens"},
{data.ei and "seiz" or "soiz","sez","siez"},"sent"}
if impsub and rfind(impsub, "/") then
inflect_tense(data, "impf_subj", "", split_multipart(impsub))
elseif impsub == "-" then
return
elseif impsub then
inflect_tense(data, "impf_subj", impsub, impsub_endings)
-- need the % here to escape the dash, which otherwise stands for a
-- character range???????
elseif pty and rfind(pty, "^弱變化%-a") then
inflect_tense(data, "impf_subj", steme,
{"asse","asses","ast", {"issons","issiens"},
{data.ei and "isseiz" or "issoiz","issez","issiez"},"assent"})
elseif pty then
inflect_tense(data, "impf_subj", pret2s, impsub_endings)
end
end
-- Add to DATA the endings for the preterite and imperfect subjunctive,
-- based on the strong and weak stems and preterite ending type(s) given in
-- ARGS. For each weak preterite ending type 'prettypeN' there should be a
-- corresponding stem in 'pretN' (defaulting to STEM) and for each strong
-- preterite ending type 'prettypeN' there should be corresponding unstressed
-- and stressed stems in 'pretuN' and 'pretsN' (defaulting to 'pretN' or STEM).
-- If specifically 'pret' and 'prettype' are unspecified, substitute defaults,
-- specifically the fallback preterite type FBPTY and stressed/unstressed
-- fallback stem FBSTEM.
function handle_pret_impf_subj(args, data, stem, fbstem, fbpty)
local all_props =
get_args(args, {"prettype", "pret", "pretu", "prets", "impsub"})
for i, props in ipairs(all_props) do
local thestem = stem
local pty = props[1][1]
local pret = props[2][1]
local pretu = props[3][1]
local prets = props[4][1]
local impsub = props[5][1]
if i == 1 and not pty and not pret then
thestem = fbstem
pty = fbpty
pret = fbstem
pretu = fbstem
prets = fbstem
end
inflect_pret_impf_subj(data, thestem, pty, pret, pretu, prets, impsub)
end
end
-- Add to DATA the forms for up to one paradigm of the future and conditional,
-- based on the future stem in STEM (possibly nil).
function inflect_future_cond(data, stem)
if stem then
if rfind(stem, "/") then
inflect_tense(data, "futr_indc", "", split_multipart(stem))
else
if data.old then
inflect_tense(data, "futr_indc", stem,
{"ai","as","aṭ","ons",{"eiz", "ez"},"ont"})
inflect_tense(data, "cond", stem,
{"eie","eies","eit", "iiens","iiez","eient"})
elseif data.ei then
inflect_tense(data, "futr_indc", stem,
{"ai","as","a","ons",{"eiz", "ez"},"ont"})
inflect_tense(data, "cond", stem,
{"eie","eies","eit",
{"iiens","iens"},{"iiez","iez"},"eient"})
else
inflect_tense(data, "futr_indc", stem,
{"ai","as","a","ons",{"oiz","eiz", "ez"},"ont"})
inflect_tense(data, "cond", stem,
{{"oie","eie"},{"oies","eies"},{"oit","eit"},
{"iiens","iens"},{"iiez","iez"},{"oient","eient"}})
end
end
end
end
-- Add to DATA all the forms for the future and conditional based on the
-- future stem(s) in ARGS. Use If no future stems given, use STEM.
function handle_future_cond(args, data, stem)
local fut_args = get_args(args, "fut")
-- Just override the value from args["fut"] if necessary
if not args["fut"] then
fut_args[1][1] = stem
end
for _, futarg in ipairs(fut_args) do
local stem = futarg[1]
inflect_future_cond(data, stem)
end
end
-- Add to DATA the forms for up to one imperfect paradigm, corresponding to
-- the imperfect stem in IMPERF (possibly nil), the corresponding ier= value
-- in IMPERFIER, the corresponding present stem in PRES (possibly nil), and
-- its corresponding ier= value in PRESIER. Only do something if either IMPERF
-- or PRES is non-nil.
function inflect_imperfect(data, group, imperf, imperfier, pres, presier)
if imperf and rfind(imperf, "/") then
inflect_tense(data, "impf_indc", "", split_multipart(imperf))
else
if not imperf and pres and not rfind(pres, "/") then
imperf = pres
imperfier = presier
end
if imperf and imperf ~= "-" then
local steme = imperf
local stema = steme_to_stema(steme)
local i = imperfier and "i" or ""
if group == "i" and data.old then
inflect_tense(data, "impf_indc", steme,
{{"eie", "oue", i .. "eve"},
{"eies", "oues", i .. "eves"},
{"eit", "out", i .. "eveṭ"},
{"iiens"},
{"iiez"},
{"eient", "ouent", i .. "event"}
})
elseif group == "i" and data.ei then
inflect_tense(data, "impf_indc", steme,
{{"eie", "oe", i .. "eve"},
{"eies", "oes", i .. "eves"},
{"eit", "ot", i .. "eve"},
{"iiens", "iens"},
{"iiez", "iez"},
{"eient", "oent", i .. "event"}
})
elseif group == "i" then
inflect_tense(data, "impf_indc", steme,
{{"oie", "eie", "oe", i .. "eve"},
{"oies", "eies", "oes", i .. "eves"},
{"oit", "eit", "ot", i .. "eve"},
{"iiens", "iens"},
{"iiez", "iez"},
{"oient", "eient", "oent", i .. "event"}
})
elseif group == "ii" and data.old then
inflect_tense(data, "impf_indc", join(steme, "iss"),
{"eie",
"eies",
"eit",
"iiens",
"iiez",
"eient"
})
elseif group == "ii" and data.ei then
inflect_tense(data, "impf_indc", join(steme, "iss"),
{"eie",
"eies",
"eit",
{"iiens", "iens"},
{"iiez", "iez"},
"eient"
})
elseif group == "ii" then
inflect_tense(data, "impf_indc", join(steme, "iss"),
{{"oie", "eie"},
{"oies", "eies"},
{"oit", "eit"},
{"iiens", "iens"},
{"iiez", "iez"},
{"oient", "eient"}
})
elseif group == "iii" and data.old then
inflect_tense(data, "impf_indc", steme,
{"eie",
"eies",
"eit",
"iiens",
"iiez",
"eient"
})
elseif group == "iii" and data.ei then
inflect_tense(data, "impf_indc", steme,
{"eie",
"eies",
"eit",
{"iiens", "iens"},
{"iiez", "iez"},
"eient"
})
elseif group == "iii" then
inflect_tense(data, "impf_indc", steme,
{{"oie", "eie"},
{"oies", "eies"},
{"oit", "eit"},
{"iiens", "iens"},
{"iiez", "iez"},
{"oient", "eient"}
})
end
end
end
end
-- Add to DATA all the forms for the imperfect based on the
-- imperfect stem(s) in ARGS. If no imperfect stems given, use
-- corresponding present stems if they exist, but use STEME and IER
-- for the first present stem and ier= value, since they're computed
-- specially.
function handle_imperfect(args, data, group, steme, ier)
local imperf_args = get_args(args, {"imperf","imperfier","pres","ier"})
-- Just override the values from args["pres"] and args["ier"]
imperf_args[1][3][1] = steme
imperf_args[1][4][1] = ier
for _, stem in ipairs(imperf_args) do
local imperf = stem[1][1]
local imperfier = stem[2][1]
local pres = stem[3][1]
local presier = stem[4][1]
inflect_imperfect(data, group, imperf, imperfier, pres, presier)
end
end
-- Add to DATA up to one form of the present participle based on the
-- present stem in PRES. Only do anything if PRES is non-nil and not a
-- multipart specification.
function inflect_pres_part(data, group, pres)
if pres and not rfind(pres, "/") then
if group == "ii" then
table.insert(data.forms.pres_ptc, join(pres, "issant"))
else
table.insert(data.forms.pres_ptc, join(pres, "ant"))
end
end
end
-- Add to DATA the forms of the present participle based on the
-- present stem(s) in ARGS. Use STEME for the first present stem,
-- since it's computed specially.
function handle_pres_part(args, data, group, steme)
if data.forms.pres_ptc == nil then
data.forms.pres_ptc = {}
end
local ppart_args = get_args(args, {"pres"})
-- Just override the value from args["pres"]
ppart_args[1][1][1] = steme
for _, stem in ipairs(ppart_args) do
local pres = stem[1][1]
inflect_pres_part(data, group, pres)
end
end
-- Add to DATA the endings for an -er or -ier verb, based on the arguments
-- in ARGS.
inflections["i"] = function(args, data)
local prese = data.prese
local press = args["press"] or prese
local i = data.ier and "i" or ""
if data.ier then
data.comment = "這個動詞變位為以''-ier''結尾的第一組動詞,帶有一個硬腭音詞幹。這些動詞的變位形式大多和以''-er''結尾的動詞類似,但某些詞在詞尾的''e''之前要額外加上一個''i''。"
else
data.comment = "這個動詞變位為以''-er''結尾的第一組動詞。"
end
data.comment = data.comment ..
verb_comment(args, "i", prese, press, data.ier, data.supe, data.old)
data.group = {"一"}
handle_pres_part(args, data, "i", prese)
data.forms.past_ptc = {join(prese, i .. "é" .. (data.old and "ṭ" or ""))}
handle_pres(args, data, "i", prese, press)
handle_imperfect(args, data, "i", prese, data.ier)
handle_pret_impf_subj(args, data, prese, prese,
data.ier and "弱變化-a2" or "弱變化-a")
handle_future_cond(args, data, join(prese, "er"))
end
-- Add to DATA the endings for a type 2 verb (-ir, with -iss- infix),
-- based on the arguments in ARGS.
inflections["ii"] = function(args, data)
local prese = data.prese
local press = args["press"] or prese
data.comment = "本動詞按照第二組動詞(以 ''-ir'' 結尾,帶 ''-iss-'' 中綴)的模式變位。 "
data.group = {"二"}
handle_pres_part(args, data, "ii", prese)
local oldt = data.old and "ṭ" or ""
data.forms.past_ptc = {join(prese, "i" .. oldt)}
handle_pres(args, data, "ii", prese, press)
handle_imperfect(args, data, "ii", prese, data.ier)
handle_pret_impf_subj(args, data, prese, prese, "弱變化-i")
handle_future_cond(args, data, join(prese, "ir"))
end
-- Add to DATA the endings for a type 3 verb (-ir without -iss- infix, or
-- -re or -oir), based on the arguments in ARGS.
inflections["iii"] = function(args, data)
local prese = data.prese
local press = args["press"] or prese
local i = data.ier and "i" or ""
data.comment = "本動詞按照第三組動詞的模式進行變位。"
if data.ier then
data.comment = data.comment .. "本動詞以腭音詞幹結尾,所以在部分變位形式末尾的 ''e'' 前要多加一個 ''i''。"
end
data.comment = data.comment ..
verb_comment(args, "iii", prese, press, data.ier, data.supe, data.old)
data.group = {"三"}
handle_pres_part(args, data, "iii", prese)
-- retrieve the infinitive without any affix, so that when
-- we put the affix back on, we don't get a doubled affix
local infinitive = data.inf_no_affix
local oldt = data.old and "ṭ" or ""
if rfind(infinitive, "[^eo]ir$") then
data.forms.past_ptc = {join(prese, "i" .. oldt)}
elseif rfind(infinitive, "ïr$") then
data.forms.past_ptc = {join(prese, "ï" .. oldt)}
else
data.forms.past_ptc = {join(prese, "u" .. oldt)}
end
handle_pres(args, data, "iii", prese, press)
handle_imperfect(args, data, "iii", prese, data.ier)
if rfind(infinitive, "[dt]re$") then
handle_pret_impf_subj(args, data, prese, prese, "弱變化-i2")
elseif rfind(infinitive, "re$") then
local pretstem = add_s(prese, false, false, data.old)
pretstem = rsub(pretstem, "s$", "")
handle_pret_impf_subj(args, data, prese, pretstem, "強變化-st")
elseif rfind(infinitive, "[eo]ir$") then
handle_pret_impf_subj(args, data, prese, prese, "弱變化-u")
else
handle_pret_impf_subj(args, data, prese, prese, "弱變化-i")
end
if rfind(infinitive, "[^eolrs]ir$") then
handle_future_cond(args, data, join(prese, "ir"))
else -- -re, -oir, -eir, -ïr, -lir, -sir, -rir
handle_future_cond(args, data, add_r(prese, data.old))
end
end
-- Add to DATA the endings for a type 3/2 verb (-ir without or with
-- -iss- infix), based on the arguments in ARGS.
inflections["iii-ii"] = function(args, data)
local prese = data.prese
local press = args["press"] or prese
local i = data.ier and "i" or ""
data.comment = "This verb conjugates as a third-group or second-group verb (ending in ''-ir'', without or with an ''-iss-'' infix). "
if data.ier then
data.comment = data.comment .. "This verb ends in a palatal stem, so there is an extra ''i'' before the ''e'' of some endings. "
end
data.comment = data.comment ..
verb_comment(args, "iii", prese, press, data.ier, data.supe, data.old)
data.group = {"三", "二", "三-第二"}
handle_pres_part(args, data, "iii", prese)
handle_pres_part(args, data, "ii", prese)
-- retrieve the infinitive without any affix, so that when
-- we put the affix back on, we don't get a doubled affix
local infinitive = data.inf_no_affix
local oldt = data.old and "ṭ" or ""
data.forms.past_ptc = {join(prese, "i" .. oldt)}
handle_pres(args, data, "iii", prese, press)
handle_pres(args, data, "ii", prese, press)
handle_imperfect(args, data, "iii", prese, data.ier)
handle_imperfect(args, data, "ii", prese, data.ier)
handle_pret_impf_subj(args, data, prese, prese, "弱變化-i")
handle_future_cond(args, data, join(prese, "ir"))
end
-- Shows the table with the given forms
function make_table(data)
return data.comment .. [=[古法語的變位因時代和地域而異。以下變位僅供參考。
<div class="NavFrame" style="clear:both">
<div class="NavHead" align=left> ]=] .. m_links.full_link({lang = lang, alt = data.forms.infinitive[1]}, "term") .. [=[
的變位<span style="font-size:90%;">(另見[[Appendix:古法語動詞]])</span></div>
<div class="NavContent" align=center>
{| style="width: 100%; background:#F0F0F0;border-collapse:separate;border-spacing:2px" class="inflection-table"
|-
! colspan="2" style="background:#e2e4c0" |
! colspan="3" style="background:#e2e4c0" | 簡單
! colspan="3" style="background:#e2e4c0" | 複合
|-
! colspan="2" style="background:#e2e4c0" | 不定式
| colspan="3" | ]=] .. show_form(data.forms.infinitive) .. [=[
| colspan="3" | ]=] .. show_form(data.forms.aux) .. " " .. show_form(data.forms.past_ptc) .. [=[
|-
! colspan="2" style="background:#e2e4c0" | 動名詞
| colspan="3" | en ]=] .. show_form(data.forms.pres_ptc) .. [=[
| colspan="3" | 用]=] .. show_form(data.forms.aux) .. [=[的動名詞後接過去分詞
|-
! colspan="2" style="background:#e2e4c0" | 現在分詞
| colspan="3" | ]=] .. show_form(data.forms.pres_ptc) .. [=[
|-
! colspan="2" style="background:#e2e4c0" | 過去分詞
| colspan="3" | ]=] .. show_form(data.forms.past_ptc) .. [=[
|-
! colspan="2" rowspan="2" style="background:#C0C0C0" | 人稱
! colspan="3" style="background:#C0C0C0" | 單數
! colspan="3" style="background:#C0C0C0" | 複數
|-
! style="background:#C0C0C0;width:12.5%" | 第一
! style="background:#C0C0C0;width:12.5%" | 第二
! style="background:#C0C0C0;width:12.5%" | 第三
! style="background:#C0C0C0;width:12.5%" | 第一
! style="background:#C0C0C0;width:12.5%" | 第二
! style="background:#C0C0C0;width:12.5%" | 第三
|-
! style="background:#c0cfe4" colspan="2" | 直陳
! style="background:#c0cfe4" | jo
! style="background:#c0cfe4" | tu
! style="background:#c0cfe4" | il
! style="background:#c0cfe4" | nos
! style="background:#c0cfe4" | vos
! style="background:#c0cfe4" | il
|-
! rowspan="5" style="background:#c0cfe4" | 簡單<br>時態
! style="height:3em;background:#c0cfe4" | 現在
| ]=] .. show_form(data.forms.pres_indc_1sg) .. [=[
| ]=] .. show_form(data.forms.pres_indc_2sg) .. [=[
| ]=] .. show_form(data.forms.pres_indc_3sg) .. [=[
| ]=] .. show_form(data.forms.pres_indc_1pl) .. [=[
| ]=] .. show_form(data.forms.pres_indc_2pl) .. [=[
| ]=] .. show_form(data.forms.pres_indc_3pl) .. [=[
|-
! style="height:3em;background:#c0cfe4" | 未完成
| ]=] .. show_form(data.forms.impf_indc_1sg) .. [=[
| ]=] .. show_form(data.forms.impf_indc_2sg) .. [=[
| ]=] .. show_form(data.forms.impf_indc_3sg) .. [=[
| ]=] .. show_form(data.forms.impf_indc_1pl) .. [=[
| ]=] .. show_form(data.forms.impf_indc_2pl) .. [=[
| ]=] .. show_form(data.forms.impf_indc_3pl) .. [=[
|-
! style="height:3em;background:#c0cfe4" | 過去
| ]=] .. show_form(data.forms.pret_indc_1sg) .. [=[
| ]=] .. show_form(data.forms.pret_indc_2sg) .. [=[
| ]=] .. show_form(data.forms.pret_indc_3sg) .. [=[
| ]=] .. show_form(data.forms.pret_indc_1pl) .. [=[
| ]=] .. show_form(data.forms.pret_indc_2pl) .. [=[
| ]=] .. show_form(data.forms.pret_indc_3pl) .. [=[
|-
! style="height:3em;background:#c0cfe4" | 將來
| ]=] .. show_form(data.forms.futr_indc_1sg) .. [=[
| ]=] .. show_form(data.forms.futr_indc_2sg) .. [=[
| ]=] .. show_form(data.forms.futr_indc_3sg) .. [=[
| ]=] .. show_form(data.forms.futr_indc_1pl) .. [=[
| ]=] .. show_form(data.forms.futr_indc_2pl) .. [=[
| ]=] .. show_form(data.forms.futr_indc_3pl) .. [=[
|-
! style="height:3em;background:#c0cfe4" | 條件
| ]=] .. show_form(data.forms.cond_1sg) .. [=[
| ]=] .. show_form(data.forms.cond_2sg) .. [=[
| ]=] .. show_form(data.forms.cond_3sg) .. [=[
| ]=] .. show_form(data.forms.cond_1pl) .. [=[
| ]=] .. show_form(data.forms.cond_2pl) .. [=[
| ]=] .. show_form(data.forms.cond_3pl) .. [=[
|-
! rowspan="5" style="background:#c0cfe4" | 複合<br>時態
! style="height:3em;background:#c0cfe4" | 現在完成
! colspan="6" style="background:#C0C0C0" | 用]=] .. show_form(data.forms.aux) .. [=[的現在時後接過去分詞
|-
! style="height:3em;background:#c0cfe4" | 愈過去
! colspan="6" style="background:#C0C0C0" | 用]=] .. show_form(data.forms.aux) .. [=[的未完成式後接過去分詞
|-
! style="height:3em;background:#c0cfe4" | 先過去
! colspan="6" style="background:#C0C0C0" | 用]=] .. show_form(data.forms.aux) .. [=[的過去時後接過去分詞
|-
! style="height:3em;background:#c0cfe4" | 將來完成
! colspan="6" style="background:#C0C0C0" | 用]=] .. show_form(data.forms.aux) .. [=[的將來時後接過去分詞
|-
! style="height:3em;background:#c0cfe4" | 條件完成
! colspan="6" style="background:#C0C0C0" | 用]=] .. show_form(data.forms.aux) .. [=[的條件式後接過去分詞
|-
! style="background:#c0e4c0" colspan="2" | 虛擬
! style="background:#c0e4c0" | que jo
! style="background:#c0e4c0" | que tu
! style="background:#c0e4c0" | qu’il
! style="background:#c0e4c0" | que nos
! style="background:#c0e4c0" | que vos
! style="background:#c0e4c0" | qu’il
|-
! rowspan="2" style="background:#c0e4c0" | 簡單<br>時態
! style="height:3em;background:#c0e4c0" | 現在
| ]=] .. show_form(data.forms.pres_subj_1sg) .. [=[
| ]=] .. show_form(data.forms.pres_subj_2sg) .. [=[
| ]=] .. show_form(data.forms.pres_subj_3sg) .. [=[
| ]=] .. show_form(data.forms.pres_subj_1pl) .. [=[
| ]=] .. show_form(data.forms.pres_subj_2pl) .. [=[
| ]=] .. show_form(data.forms.pres_subj_3pl) .. [=[
|-
! style="height:3em;background:#c0e4c0" rowspan="1" | 未完成
| ]=] .. show_form(data.forms.impf_subj_1sg) .. [=[
| ]=] .. show_form(data.forms.impf_subj_2sg) .. [=[
| ]=] .. show_form(data.forms.impf_subj_3sg) .. [=[
| ]=] .. show_form(data.forms.impf_subj_1pl) .. [=[
| ]=] .. show_form(data.forms.impf_subj_2pl) .. [=[
| ]=] .. show_form(data.forms.impf_subj_3pl) .. [=[
|-
! rowspan="2" style="background:#c0e4c0" | 複合<br>時態
! style="height:3em;background:#c0e4c0" | 過去
! colspan="6" style="background:#C0C0C0" | 用]=] .. show_form(data.forms.aux) .. [=[的現在時虛擬語氣後接過去分詞
|-
! rowspan="1" style="height:3em;background:#c0e4c0" | 愈過去
! colspan="6" style="background:#C0C0C0" | 用]=] .. show_form(data.forms.aux) .. [=[的未完成虛擬語氣後接過去分詞
|-
! colspan="2" rowspan="2" style="height:3em;background:#e4d4c0" | 命令
! style="background:#e4d4c0" | –
! style="background:#e4d4c0" | tu
! style="background:#e4d4c0" | –
! style="background:#e4d4c0" | nos
! style="background:#e4d4c0" | vos
! style="background:#e4d4c0" | –
|-
| —
| ]=] .. show_form(data.forms.impr_2sg) .. [=[
| —
| ]=] .. show_form(data.forms.impr_1pl) .. [=[
| ]=] .. show_form(data.forms.impr_2pl) .. [=[
| —
|-
|}</div></div>]=]
end
-- Create a link for a form
function make_link(subform)
return m_links.full_link({lang = lang, term = subform})
end
-- Shows forms, or a dash if empty
function show_form(subforms)
if not subforms then
return "—"
elseif type(subforms) ~= "table" then
error("a non-table value was given in the list of inflected forms.")
elseif #subforms == 0 then
return "—"
end
-- Concatenate results, omitting duplicates
local ret = {}
for key, subform in ipairs(subforms) do
insert_if_not(ret, subform)
end
return table.concat(ret, ", ")
end
return export
-- For Vim, so we get 4-space tabs
-- vim: set ts=4 sw=4 noet: