pashto-inflector/src/lib/verb-info.ts

938 lines
34 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Copyright (c) 2021 lingdocs.com
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
import {
concatPsString,
firstPhonetics,
makePsString,
psStringEquals,
removeEndingL,
yulEndingInfinitive,
removeRetroflexR,
inflectYey,
unisexInfToObjectMatrix,
complementInflects,
beginsWithDirectionalPronoun,
checkForOoPrefix,
removeStartingTick,
ensureShortWurShwaShift,
choosePersInf,
} from "./p-text-helpers";
import {
accentOnFront,
accentOnNFromEnd,
removeAccents,
} from "./accent-helpers";
import {
inflectWord,
} from "./pashto-inflector";
import {
stativeAux,
} from "./irregular-conjugations";
import {
presentParticipleSuffix
} from "./grammar-units";
import {
dynamicAuxVerbs,
} from "./dyn-comp-aux-verbs";
import {
getPersonInflectionsKey,
getPersonNumber,
spaceInForm,
getAuxTransitivity,
chooseParticipleInflection,
} from "./misc-helpers";
import * as T from "../types";
const eyEndingUnaccented: T.PsString = { p: "ی", f: "ey" };
/**
* Compiles the base information (roots, stems etc.) needed in order
* to make all the conjugations. This is the first step of creating
* the conjugations.
*
* @param entry - the dictionary entry for the verb
* @param complement - the dictioanry entry for the complement of the verb if compound
*/
export function getVerbInfo(
entry: T.DictionaryEntry,
complement?: T.DictionaryEntry,
): T.VerbInfo {
const type = getType(entry);
if (type === "transitive or grammatically transitive simple") {
return {
type: "transitive or grammatically transitive simple",
transitive: getVerbInfo(
// @ts-ignore (will have entry.c)
{ ...entry, c: entry.c.replace("trans./gramm. trans.", "trans.") },
) as T.SimpleVerbInfo,
grammaticallyTransitive: getVerbInfo(
// @ts-ignore (will have entry.c)
{ ...entry, c: entry.c.replace("trans./gramm. trans.", "gramm. trans.") },
) as T.SimpleVerbInfo,
};
}
const transitivity = getTransitivity(entry);
if (type !== "simple") {
if (!complement) {
throw new Error("complement required for compound verb");
}
if (type === "dynamic compound") {
return getDynamicCompoundInfo(entry, complement)
}
if (type === "dynamic or stative compound") {
return {
type: "dynamic or stative compound",
transitivity,
dynamic: getDynamicCompoundInfo(
// @ts-ignore (will have entry.c)
{ ...entry, c: entry.c.replace("dyn./stat.", "dyn.") },
complement,
),
stative: getVerbInfo(
// @ts-ignore (will have entry.c)
{ ...entry, c: entry.c.replace("dyn./stat.", "stat.") },
complement,
) as T.StativeCompoundVerbInfo,
};
}
if (type === "dynamic or generative stative compound") {
return {
type: "dynamic or generative stative compound",
transitivity,
dynamic: getDynamicCompoundInfo(
// @ts-ignore (will have entry.c)
{ ...entry, c: entry.c.replace("gen. stat./dyn.", "dyn.") },
complement,
),
stative: getGenerativeStativeCompoundVerbInfo(
// @ts-ignore (will have entry.c)
{ ...entry, c: entry.c.replace("gen. stat./dyn.", "gen. stat.") },
complement,
),
}
}
if (type === "generative stative compound") {
return getGenerativeStativeCompoundVerbInfo(entry, complement as T.DictionaryEntry);
}
}
const comp = complement ? ensureUnisexInflections(complement) : undefined;
const root = getVerbRoots(entry, transitivity, comp);
const stem = getVerbStems(entry, root, transitivity, comp);
const infinitive = "mascSing" in root.imperfective ? root.imperfective.mascSing.long : root.imperfective.long;
const yulEnding = yulEndingInfinitive(infinitive);
const participle = getParticiple(entry, stem, infinitive, transitivity, comp);
const idiosyncraticThirdMascSing = getIdiosyncraticThirdMascSing(entry);
const baseInfo: T.VerbInfoBase = {
transitivity,
yulEnding,
root,
stem,
participle,
...idiosyncraticThirdMascSing ? {
idiosyncraticThirdMascSing,
} : {},
};
if (type === "stative compound") {
return {
...baseInfo,
type,
complement: comp as T.UnisexInflections,
};
}
return {
...baseInfo,
type,
};
}
type Bases = {
stem: {
imperfective: T.FullForm<T.PsString>,
perfective: T.FullForm<T.PsString>,
perfectiveSplit?: T.SplitInfo,
},
root: {
imperfective: T.FullForm<T.PsString>,
perfective: T.FullForm<T.PsString>,
perfectiveSplit?: T.SplitInfo,
},
participle: {
present: T.FullForm<T.PsString>,
past: T.FullForm<T.PsString>,
},
}
function getGenerativeStativeCompoundVerbInfo(
entry: T.DictionaryEntry, comp: T.DictionaryEntry, forceSingular?: true,
): T.GenerativeStativeCompoundVerbInfo {
const transitivity = getTransitivity(entry);
const transitivityNoGrammTrans = transitivity === "grammatically transitive" ? "transitive" : transitivity;
const yulEnding = null;
const objComplement = getObjComplementInfo(entry, comp, forceSingular);
const auxVerb = stativeAux[transitivityNoGrammTrans];
const compUsed = objComplement.plural ? objComplement.plural : makePsString(
objComplement.entry.p,
firstPhonetics(objComplement.entry.f),
);
const bases: Bases = {
stem: {
imperfective: auxVerb.info.stem.imperfective,
perfective: auxVerb.info.stem.perfective,
},
root: {
imperfective: auxVerb.info.root.imperfective,
perfective: auxVerb.info.root.perfective,
},
participle: {
present: auxVerb.info.participle.present,
past: chooseParticipleInflection(
inflectYey(
"mascSing" in auxVerb.info.participle.past
// purely for type saftey, will not have mascSing
// in a non stative compound verb
/* istanbul ignore next */
? auxVerb.info.participle.past.mascSing
: auxVerb.info.participle.past
),
objComplement.person,
),
}
}
const perfectiveStem = concatPsString(compUsed, " ", bases.stem.perfective);
const stem = {
imperfective: concatPsString(compUsed, " ", bases.stem.imperfective),
perfective: perfectiveStem,
perfectiveSplit: splitPerfective(perfectiveStem, 0, 0, true),
};
const perfectiveRoot = concatPsString(compUsed, " ", bases.root.perfective) as T.OptionalPersonInflections<T.LengthOptions<T.PsString>>;
const root = {
imperfective: concatPsString(compUsed, " ", bases.root.imperfective) as T.OptionalPersonInflections<T.LengthOptions<T.PsString>>,
perfective: perfectiveRoot,
perfectiveSplit: splitPerfective(perfectiveRoot, 0, 0, true),
};
const participle = {
present: concatPsString(compUsed, " ", auxVerb.info.participle.present),
past: concatPsString(compUsed, " ", bases.participle.past),
}
return {
type: "generative stative compound",
transitivity,
yulEnding,
stem,
root,
participle,
objComplement,
...objComplement.plural ? {
singularForm: getGenerativeStativeCompoundVerbInfo(entry, comp, true),
} : {},
};
}
function getDynamicCompoundInfo(entry: T.DictionaryEntry, comp: T.DictionaryEntry, forceSingular?: true): T.DynamicCompoundVerbInfo {
const transitivity = getTransitivity(entry);
const yulEnding = null;
const objComplement = getObjComplementInfo(entry, comp, forceSingular);
const auxVerb = getDynamicAuxVerb(entry);
const auxVerbInfo = getVerbInfo(auxVerb.entry, auxVerb.complement) as T.NonComboVerbInfo;
const compUsed = objComplement.plural ? objComplement.plural : makePsString(
objComplement.entry.p,
firstPhonetics(objComplement.entry.f),
);
const bases: Bases = (auxVerbInfo.type === "stative compound")
? getObjectMatchingBases(auxVerbInfo, objComplement.person)
: {
stem: {
imperfective: auxVerbInfo.stem.imperfective,
perfective: auxVerbInfo.stem.perfective,
...auxVerbInfo.stem.perfectiveSplit ? {
perfectiveSplit: auxVerbInfo.stem.perfectiveSplit,
} : {},
},
root: {
imperfective: auxVerbInfo.root.imperfective,
perfective: auxVerbInfo.root.perfective,
...auxVerbInfo.root.perfectiveSplit ? {
perfectiveSplit: auxVerbInfo.root.perfectiveSplit,
} : {},
},
participle: {
present: auxVerbInfo.participle.present,
past: chooseParticipleInflection(
inflectYey(
"mascSing" in auxVerbInfo.participle.past
// purely for type saftey, will not have mascSing
// in a non stative compound verb
/* istanbul ignore next */
? auxVerbInfo.participle.past.mascSing
: auxVerbInfo.participle.past
),
objComplement.person,
),
}
}
const stem = {
imperfective: concatPsString(compUsed, " ", bases.stem.imperfective),
perfective: concatPsString(compUsed, " ", bases.stem.perfective),
...bases.stem.perfectiveSplit ? {
perfectiveSplit: makeDynamicPerfectiveSplit(compUsed, bases.stem.perfectiveSplit),
} : {},
};
const root = {
imperfective: concatPsString(compUsed, " ", bases.root.imperfective) as T.OptionalPersonInflections<T.LengthOptions<T.PsString>>,
perfective: concatPsString(compUsed, " ", bases.root.perfective) as T.OptionalPersonInflections<T.LengthOptions<T.PsString>>,
...bases.root.perfectiveSplit ? {
perfectiveSplit: makeDynamicPerfectiveSplit(compUsed, bases.root.perfectiveSplit),
} : {},
};
const participle = {
present: concatPsString(compUsed, " ", auxVerbInfo.participle.present),
past: concatPsString(compUsed, " ", bases.participle.past),
};
const makeIntransitiveFormOfEntry = (e: T.DictionaryEntry): T.DictionaryEntry => ({
...e,
p: e.p.replace(
"کول",
"کېدل"
),
e: e.e.replace("to do", "to become"),
f: e.f.replace(/kaw[u|ú]l/, "kedul"),
c: "v. intrans. dyn. comp.",
});
const intransitiveFormEntry = (transitivity === "transitive" && auxVerb.entry.p === "کول")
? makeIntransitiveFormOfEntry(entry)
: null;
return {
type: "dynamic compound",
transitivity,
yulEnding,
stem,
root,
participle,
objComplement,
auxVerb: auxVerb.entry,
...auxVerb.complement ? {
auxVerbComplement: auxVerb.complement,
} : {},
...objComplement.plural ? {
singularForm: getDynamicCompoundInfo(entry, comp, true),
} : {},
...intransitiveFormEntry ? {
intransitiveForm: getDynamicCompoundInfo(intransitiveFormEntry, comp),
} : {},
};
}
function getObjectMatchingBases(auxInfo: T.NonComboVerbInfo, person: T.Person): Bases {
const key = getPersonInflectionsKey(person);
const getBase = (x: T.FullForm<T.PsString>): T.SingleOrLengthOpts<T.PsString> => (
"mascSing" in x ? x[key] : x
);
return {
stem: {
imperfective: getBase(auxInfo.stem.imperfective),
perfective: getBase(auxInfo.stem.perfective),
...auxInfo.stem.perfectiveSplit ? {
perfectiveSplit: choosePersInf(auxInfo.stem.perfectiveSplit, key),
} : {},
},
root: {
imperfective: getBase(auxInfo.root.imperfective),
perfective: getBase(auxInfo.root.perfective),
...auxInfo.root.perfectiveSplit ? {
perfectiveSplit: choosePersInf(auxInfo.root.perfectiveSplit, key),
} : {},
},
participle: {
present: getBase(auxInfo.participle.present),
past: getBase(auxInfo.participle.past),
},
};
}
function getObjComplementInfo(
entry: T.DictionaryEntry,
complement: T.DictionaryEntry,
forceSingular?: true
): T.ObjComplement {
const complementInEntry = makePsString(
entry.p.split(" ")[0],
entry.f.split(" ")[0],
);
const complementEntry: T.DictionaryEntry = { ...complement, f: firstPhonetics(complement.f) };
const usesSeperatePluralForm = !forceSingular && !psStringEquals(
makePsString(complementInEntry.p, removeAccents(complementInEntry.f)),
makePsString(complementEntry.p, removeAccents(complementEntry.f)),
);
return {
entry: complementEntry,
...usesSeperatePluralForm ? {
plural: complementInEntry,
} : {},
person: getComplementPerson(complement, usesSeperatePluralForm),
};
}
function getTransitivity(entry: T.DictionaryEntry): T.Transitivity {
if (!entry.c) {
throw new Error("No part of speech info");
}
if (entry.c.includes("gramm. trans.")) {
return "grammatically transitive";
}
if (entry.c.includes("intrans.")) {
return "intransitive";
}
return "transitive";
}
function getType(entry: T.DictionaryEntry):
"simple" | "stative compound" | "dynamic compound" |
"dynamic or stative compound" | "dynamic or generative stative compound" |
"generative stative compound" | "transitive or grammatically transitive simple"
{
// error will have thrown before on the getTransitivity function if missing entry.c
/* istanbul ignore if */
if (!entry.c) {
throw new Error("No part of speech info");
}
if (entry.c.includes(" trans./gramm. trans.")) {
return "transitive or grammatically transitive simple";
}
if (entry.c.includes(" gen. stat. comp.")) {
return "generative stative compound";
}
if (entry.c.includes(" stat. comp.")) {
return "stative compound";
}
if (entry.c.includes(" dyn. comp.")) {
return "dynamic compound";
}
if (entry.c.includes(" dyn./stat. comp.")) {
return "dynamic or stative compound";
}
if (entry.c.includes(" gen. stat./dyn. comp.")) {
return "dynamic or generative stative compound";
}
return "simple";
}
function getIdiosyncraticThirdMascSing(entry: T.DictionaryEntry): T.ShortThirdPersFormSet | false {
if (entry.tppp && entry.tppf) {
const tpp = makePsString(entry.tppp, entry.tppf);
const ooRes = addOoPrefix(tpp, entry)
return {
imperfective: tpp,
perfective: ooRes.ps as T.PsString,
};
}
return false;
}
/**
* Returns the roots (imperfective and perfective) of a given verb
*
* @param entry - the dictionary entry for the verb
*/
function getVerbRoots(entry: T.DictionaryEntry, transitivity: T.Transitivity, complement?: T.UnisexInflections): T.VerbRootSet {
// each of the roots compes with a short and long version
// with or without the ending ل - ul
const isKawulAux = entry.p === "کول";
const shortAndLong = (root: T.PsString, perfective?: "perfective"): T.LengthOptions<T.PsString> => {
const long = perfective ? root : accentOnNFromEnd(root, yulEndingInfinitive(root) ? 1 : 0);
const short = removeEndingL(root);
return {
long,
short,
...(isKawulAux && perfective) ? {
mini: removeRetroflexR(short)
} : {},
};
};
const infinitive = makePsString(entry.p, firstPhonetics(entry.f));
// the imperfective root is the infinitive
// TODO: CHECK THIS!! FOR PERSON INFLECTIONS??
const imperfective = ((): T.OptionalPersonInflections<T.LengthOptions<T.PsString>> => {
// if stative compound
if (complement && spaceInForm(infinitive)) {
const comp = complementInflects(complement) ? unisexInfToObjectMatrix(complement) : complement.masc[0][0];
const t = getAuxTransitivity(transitivity);
const aux = stativeAux[t].info.root.imperfective
return concatPsString(comp, " ", aux) as T.OptionalPersonInflections<T.LengthOptions<T.PsString>>;
}
return shortAndLong(infinitive);
})();
const { perfective, pSplit, fSplit } = ((): {
perfective: T.OptionalPersonInflections<T.LengthOptions<T.PsString>>
pSplit: number,
fSplit: number,
} => {
// if stative compound
if (complement) {
const comp = complementInflects(complement) ? unisexInfToObjectMatrix(complement) : complement.masc[0][0];
const t = getAuxTransitivity(transitivity);
const aux = stativeAux[t].info.root.perfective
return {
pSplit: 0,
fSplit: 0,
perfective: concatPsString(comp, " ", aux) as T.OptionalPersonInflections<T.LengthOptions<T.PsString>>,
};
}
// the perfective root is
// - the special perfective root if it exists, or
if (entry.prp && entry.prf) {
const perfective = shortAndLong(makePsString(entry.prp, entry.prf), "perfective");
const hasOoPrefix = checkForOoPrefix(perfective.long);
return {
perfective,
pSplit: entry.separationAtP || (hasOoPrefix ? 1 : 0),
fSplit: entry.separationAtF || (hasOoPrefix ? 2 : 0),
};
}
// - the infinitive prefixed with oo
const { ps, pSplit, fSplit } = addOoPrefix(infinitive, entry);
return {
perfective: shortAndLong(ps as T.PsString, "perfective"),
pSplit,
fSplit,
};
})();
const perfectiveSplit = splitPerfective(perfective, pSplit, fSplit, !!complement);
return {
imperfective,
perfective,
perfectiveSplit,
};
}
/**
* Returns the stems (imperfective and perfective) of a given verb
*
* @param entry - the dictionary entry for the verb
*/
function getVerbStems(entry: T.DictionaryEntry, root: T.VerbRootSet, transitivity: T.Transitivity, complement?: T.UnisexInflections): T.VerbStemSet {
function isRegEdulTransitive(): boolean {
/* istanbul ignore next */
if ("mascSing" in root.imperfective) {
return false;
}
const lastPCharacters = root.imperfective.long.p.slice(-3);
return (
// @ts-ignore - will always have a entry.c if we get to this point
(entry.c.includes("intrans."))
&& (lastPCharacters === "ېدل")
);
}
function makeIntransImperfectiveStem() {
const long = {
// @ts-ignore
p: root.imperfective.long.p.slice(0, -2) + "ږ",
// @ts-ignore
f: root.imperfective.long.f.slice(0, -4) + "éG",
};
if (entry.shortIntrans) {
const short = makePsString(
long.p.slice(0, -2),
long.f.slice(0, -2),
);
return { long, short };
}
return long;
}
const imperfective = ((): T.FullForm<T.PsString> => {
const auxTransitivity = getAuxTransitivity(transitivity);
if (complement && spaceInForm(root.imperfective)) {
const comp = complementInflects(complement) ? unisexInfToObjectMatrix(complement) : complement.masc[0][0];
return concatPsString(
comp,
" ",
stativeAux[auxTransitivity].info.stem.imperfective as T.PsString,
);
}
// the imperfective stem is
// - the special present stem if it exists, or
if (entry.psp && entry.psf) {
return makePsString(entry.psp, entry.psf);
}
// - the eG form (and short form possibly) if regular transitive, or
if (isRegEdulTransitive()) {
return makeIntransImperfectiveStem()
}
// - the infinitive minus ل
return "mascSing" in root.imperfective
? root.imperfective.mascSing.short
: root.imperfective.short;
})();
const { perfective, pSplit, fSplit } = ((): { perfective: T.FullForm<T.PsString>, pSplit: number, fSplit: number } => {
if (complement) {
const comp = complementInflects(complement) ? unisexInfToObjectMatrix(complement) : complement.masc[0][0];
const t = getAuxTransitivity(transitivity);
return {
perfective: concatPsString(comp, " ", stativeAux[t].info.stem.perfective),
pSplit: 0,
fSplit: 0,
};
}
// the perfective stem is
// - the special subjunctive stem if it exists, or
if (entry.ssp && entry.ssf) {
const isKawulAux = entry.p === "کول";
const perfective = makePsString(entry.ssp, entry.ssf);
const hasOoPrefix = checkForOoPrefix(perfective);
if (isKawulAux) {
return {
perfective: {
long: perfective,
short: removeRetroflexR(perfective),
},
pSplit: hasOoPrefix ? 1 : 0,
fSplit: hasOoPrefix ? 2 : 0,
};
}
return {
perfective,
pSplit: entry.separationAtP || (hasOoPrefix ? 1 : 0),
fSplit: entry.separationAtF || (hasOoPrefix ? 2 : 0),
};
}
// - the perfective stem prefixed with oo (if possible)
const res = addOoPrefix(imperfective as T.SingleOrLengthOpts<T.PsString>, entry);
return {
perfective: res.ps,
pSplit: res.pSplit,
fSplit: res.fSplit,
};
})();
const perfectiveSplit = splitPerfective(perfective, pSplit, fSplit, !!complement);
return {
imperfective,
perfective,
...perfectiveSplit ? {
perfectiveSplit,
} : {},
};
}
function splitPerfective(perfective: T.FullForm<T.PsString>, pSplit: number, fSplit: number, isStativeComp: boolean): T.SplitInfo | undefined {
if (!isStativeComp && pSplit === 0 && fSplit === 0) {
return undefined;
}
if ("mascSing" in perfective) {
// @ts-ignore
return {
mascSing: splitPerfective(perfective.mascSing, pSplit, fSplit, isStativeComp),
mascPlur: splitPerfective(perfective.mascPlur, pSplit, fSplit, isStativeComp),
femSing: splitPerfective(perfective.femSing, pSplit, fSplit, isStativeComp),
femPlur: splitPerfective(perfective.femPlur, pSplit, fSplit, isStativeComp),
};
}
if ("long" in perfective) {
return {
// @ts-ignore
short: splitPerfective(perfective.short, pSplit, fSplit, isStativeComp) as [T.PsString, T.PsString],
long: splitPerfective(perfective.long, pSplit, fSplit, isStativeComp) as [T.PsString, T.PsString],
..."mini" in perfective ? {
// @ts-ignore
mini: splitPerfective(perfective.mini, pSplit, fSplit, isStativeComp) as [T.PsString, T.PsString],
} : {},
};
}
if (isStativeComp) {
// if it's a stative, split whatever is before the aux verb with a trailing space
const pWords = perfective.p.split(" ");
const fWords = perfective.f.split(" ");
const before = makePsString(
pWords.slice(0, -1).join(" ") + " ",
fWords.slice(0, -1).join(" ") + " ",
);
const after = makePsString(
pWords[pWords.length - 1],
fWords[fWords.length - 1],
);
return [before, after];
}
const pBeg = perfective.p.slice(0, pSplit);
const before = makePsString(
pBeg.endsWith(" ") ? pBeg.slice(0, -1) : pBeg,
perfective.f.slice(0, fSplit).replace("`", ""),
);
const beforeAccented = beginsWithDirectionalPronoun(before)
? before
: accentOnFront(before);
const after = makePsString(perfective.p.slice(pSplit), removeAccents(removeStartingTick(perfective.f.slice(fSplit))));
return [beforeAccented, after] as T.SplitInfo;
}
function getParticiple(entry: T.DictionaryEntry, stem: T.VerbStemSet, infinitive: T.PsString, transitivity: T.Transitivity, complement?: T.UnisexInflections): T.ParticipleSet {
const shortParticipleRoot = ((): T.PsString | null => {
const shortenableEndings = ["ښتل", "ستل", "وتل"];
// special thing for اېښودل - پرېښودل
if (infinitive.p.slice(-4) === "ښودل" && infinitive.p.length > 4 && infinitive.p !== "کېښودل" && infinitive.p !== "کښېښودل") {
return makePsString(
infinitive.p.slice(0, -3),
infinitive.f.slice(0, -4),
);
}
const isOrulShortenable = ["وړل", "راوړل", "وروړل"].includes(infinitive.p);
if (isOrulShortenable || (shortenableEndings.includes(infinitive.p.slice(-3)) && infinitive.p.slice(-4) !== "استل")) {
return makePsString(
infinitive.p.slice(0, -1),
infinitive.f.slice(0, -2),
)
}
return null;
})();
const makeSepStativePart = (complement: T.UnisexInflections, tense: "present" | "past"): T.FullForm<T.PsString> => {
const compInflects = complementInflects(complement);
const comp = compInflects ? unisexInfToObjectMatrix(complement) : complement.masc[0][0];
const aux = stativeAux[auxTransitivity].info.participle[tense] as T.PsString;
return concatPsString(
comp,
" ",
compInflects
? unisexInfToObjectMatrix(inflectYey(aux) as T.UnisexInflections)
: aux,
);
};
const accentPastPart = (pp: T.PsString): T.PsString => (
accentOnNFromEnd(pp, yulEndingInfinitive(infinitive) ? 2 : 1)
);
const auxTransitivity = getAuxTransitivity(transitivity);
const past = (entry.pprtp && entry.pprtf)
? makePsString(entry.pprtp, entry.pprtf)
: complement
? makeSepStativePart(complement, "past")
: shortParticipleRoot
? {
short: accentPastPart(
concatPsString(ensureShortWurShwaShift(shortParticipleRoot), eyEndingUnaccented),
),
long: accentPastPart(
concatPsString(infinitive, eyEndingUnaccented),
),
}
: accentPastPart(concatPsString(infinitive, eyEndingUnaccented));
// TODO: make this into a rule?
const shortImperfectiveRoot = (entry.p === "وتل") ? { p: "وتل", f: "watl" } : removeEndingL(infinitive);
const accentPresPart = (pp: T.PsString): T.PsString => (
accentOnNFromEnd(pp, 1)
);
const present = (complement && spaceInForm(infinitive))
? makeSepStativePart(complement, "present")
: (shortParticipleRoot && (!psStringEquals(shortParticipleRoot, shortImperfectiveRoot) || (entry.p === "وتل")))
? {
short: accentPresPart(
concatPsString(shortParticipleRoot, presentParticipleSuffix),
),
long: accentPresPart(
concatPsString(shortImperfectiveRoot, presentParticipleSuffix),
),
}
: ("short" in stem.imperfective && entry.shortIntrans && entry.p !== "اوسېدل")
? {
short: accentPresPart(
concatPsString(stem.imperfective.short, presentParticipleSuffix),
),
long: accentPresPart(
concatPsString(shortImperfectiveRoot, presentParticipleSuffix),
),
}
: accentPresPart(
concatPsString(
shortImperfectiveRoot.p === "وړ" ? ensureShortWurShwaShift(shortImperfectiveRoot) : shortImperfectiveRoot,
presentParticipleSuffix,
),
);
return {
past,
present,
};
}
/**
* Adds a perfective و - oo prefix to a verb
*
* @param entry - the dictionary entry for the verb
*/
function addOoPrefix(
s: T.SingleOrLengthOpts<T.PsString>,
entry: T.DictionaryEntry,
): { ps: T.SingleOrLengthOpts<T.PsString>, pSplit: number, fSplit: number } {
let pSplit = 0;
let fSplit = 0;
// A bit of side effects in this function... sorry!
function attachOo(ps: T.PsString): T.PsString;
function attachOo(ps: T.SingleOrLengthOpts<T.PsString>): T.SingleOrLengthOpts<T.PsString>;
function attachOo(ps: T.SingleOrLengthOpts<T.PsString>): T.SingleOrLengthOpts<T.PsString> {
if ("long" in ps) {
return {
short: attachOo(ps.short),
long: attachOo(ps.long),
};
}
if (entry.separationAtP && entry.separationAtF) {
pSplit = entry.separationAtP;
fSplit = entry.separationAtF;
return ps;
}
if (entry.noOo) {
return ps;
}
if (entry.sepOo) {
pSplit = 2;
fSplit = 3;
return {
p: `و ${ps.p}`,
f: `oo\`${ps.f}`,
};
}
const startsWithA = ps.p.charAt(0) === "ا" && ps.f.charAt(0) === "a";
if (startsWithA) {
pSplit = 2;
fSplit = 3;
return {
p: `و${ps.p}`,
f: `wa${ps.f}`,
};
}
const startsWithAa = ["آ", "ا"].includes(ps.p.charAt(0)) && ps.f.slice(0, 2) === "aa";
if (startsWithAa) {
pSplit = 2;
fSplit = 3;
return {
p: `وا${ps.p.substr(1)}`,
f: `w${ps.f}`,
};
}
const startsWithOo = ["óo", "oo"].includes(ps.f.slice(0, 2));
if (startsWithOo) {
pSplit = 1;
fSplit = 2;
return {
p: `و${ps.p}`,
f: `wU${ps.f}`,
};
}
const startsWithEe = ["ée", "ee"].includes(ps.f.slice(0, 2)) && ps.p.slice(0, 2) === "ای";
const startsWithE = ["e", "é"].includes(ps.f[0]) && ps.p.slice(0, 2) === "اې";
if (startsWithEe || startsWithE) {
pSplit = 2;
fSplit = startsWithEe ? 3 : 2;
return {
p: `و${ps.p.slice(1)}`,
f: `w${ps.f}`,
};
}
const startsWithO = ["ó", "o"].includes(ps.f[0]) && ps.p.slice(0, 2) === "او";
if (startsWithO) {
pSplit = 1;
fSplit = 2;
return {
p: `و${ps.p}`,
f: `oo\`${ps.f}`,
};
}
pSplit = 1;
fSplit = 2;
return {
p: `و${ps.p}`,
f: `oo${ps.f}`,
};
}
const attachedOo = attachOo(s);
return {
ps: accentOnFront(attachedOo),
pSplit: entry.separationAtP ? entry.separationAtP : pSplit,
fSplit: entry.separationAtF ? entry.separationAtF : fSplit,
};
}
function ensureUnisexInflections(complement: T.DictionaryEntry): T.UnisexInflections {
const inflected = inflectWord(complement);
const isUnisex = inflected && (("masc" in inflected) && ("fem" in inflected));
if (isUnisex) {
return inflected as T.UnisexInflections;
}
const word = makePsString(complement.p, firstPhonetics(complement.f));
return {
masc: [
[word],
[word],
[word],
],
fem: [
[word],
[word],
[word],
],
};
}
function getDynamicAuxVerb(entry: T.DictionaryEntry): {
entry: T.DictionaryEntry,
complement?: T.DictionaryEntry,
} {
const auxWord = entry.p.trim().split(" ").slice(-1)[0];
const auxWordResult = dynamicAuxVerbs.find((a) => a.entry.p === auxWord);
/* istanbul ignore next */
if (!auxWordResult) {
throw new Error("unknown auxilary verb for dynamic compound");
}
return {
entry: auxWordResult.entry,
...("complement" in auxWordResult) ? {
complement: auxWordResult.complement,
} : {},
};
}
function getComplementPerson(
complement: T.DictionaryEntry,
usesSeperatePluralForm?: boolean,
): T.Person {
const number = (
(complement.c && complement.c.includes("pl.")) || usesSeperatePluralForm
) ? "plural" : "singular";
const gender = (complement.c && complement.c.includes("n. m.")) ? "masc" : "fem";
return getPersonNumber(gender, number);
}
function makeDynamicPerfectiveSplit(comp: T.PsString, auxSplit: T.SplitInfo): T.SplitInfo {
if ("mascSing" in auxSplit) {
return {
mascSing: makeDynamicPerfectiveSplit(comp, auxSplit.mascSing) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>,
mascPlur: makeDynamicPerfectiveSplit(comp, auxSplit.mascPlur) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>,
femSing: makeDynamicPerfectiveSplit(comp, auxSplit.femSing) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>,
femPlur: makeDynamicPerfectiveSplit(comp, auxSplit.femPlur) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>,
};
}
if ("long" in auxSplit) {
return {
long: makeDynamicPerfectiveSplit(comp, auxSplit.long) as [T.PsString, T.PsString],
short: makeDynamicPerfectiveSplit(comp, auxSplit.short) as [T.PsString, T.PsString],
...auxSplit.mini ? {
mini: makeDynamicPerfectiveSplit(comp, auxSplit.mini) as [T.PsString, T.PsString],
} : {},
};
}
return [
concatPsString(comp, " ", auxSplit[0]),
auxSplit[1],
];
}