519 lines
18 KiB
TypeScript
519 lines
18 KiB
TypeScript
/**
|
|
* 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 {
|
|
ensureBaAt,
|
|
isAllOne,
|
|
isVerbBlock,
|
|
removeHead,
|
|
uniquePsStringArray,
|
|
splitOffLeapfrogWord,
|
|
removeObjComp,
|
|
psRemove,
|
|
psStringContains,
|
|
} from "../lib/p-text-helpers";
|
|
import {
|
|
getPersonFromVerbForm,
|
|
pickPersInf,
|
|
} from "../lib/misc-helpers";
|
|
import {
|
|
baParticle,
|
|
pronouns,
|
|
} from "../lib/grammar-units";
|
|
import {
|
|
removeAccents,
|
|
} from "../lib/accent-helpers";
|
|
import { concatPsString } from "../lib/p-text-helpers";
|
|
import * as T from "../types";
|
|
const pashtoCharacterRange = "\u0621-\u065f\u0670-\u06d3\u06d5"
|
|
|
|
function getSplitHead(split: T.SplitInfo | undefined, matrixKey: T.PersonInflectionsField) {
|
|
if (!split) {
|
|
return undefined;
|
|
}
|
|
const fromMatrix = pickPersInf(split, matrixKey)
|
|
// doesn't matter what length it is, the head will always be the same
|
|
const pair = "long" in fromMatrix ? fromMatrix.long : fromMatrix;
|
|
return pair[0];
|
|
}
|
|
|
|
function formHasVariations(form: T.VerbForm | T.ImperativeForm | T.ParticipleForm | T.SentenceForm): boolean {
|
|
if ("mascSing" in form) {
|
|
return formHasVariations(form.mascSing);
|
|
}
|
|
if ("long" in form) {
|
|
return formHasVariations(form.long);
|
|
}
|
|
if (!isVerbBlock(form)) {
|
|
return false;
|
|
}
|
|
return !isAllOne(form);
|
|
}
|
|
|
|
type Pronouns = undefined | {
|
|
subject: T.PsString | [T.PsString, T.PsString],
|
|
object?: T.PsString | [T.PsString, T.PsString],
|
|
mini: T.PsString,
|
|
}
|
|
|
|
const nuParticle = { p: "نه", f: "nú" };
|
|
|
|
export default function addPronouns({ s, subject, object, info, displayForm, intransitive, ergative, matrixKey, englishConjugation, negative, sentenceLevel = "hard" }: {
|
|
s: T.SentenceForm,
|
|
subject: T.Person,
|
|
object: T.Person,
|
|
info: T.NonComboVerbInfo,
|
|
displayForm: T.DisplayFormForSentence,
|
|
intransitive: boolean,
|
|
ergative: boolean,
|
|
matrixKey: T.PersonInflectionsField,
|
|
negative: boolean,
|
|
englishConjugation?: T.EnglishVerbConjugation,
|
|
sentenceLevel?: "easy" | "medium" | "hard",
|
|
}): T.SentenceForm {
|
|
if ("long" in s) {
|
|
return {
|
|
long: addPronouns({ s: s.long, subject, object, info, displayForm, intransitive, ergative, matrixKey, englishConjugation, negative, sentenceLevel }) as T.ArrayOneOrMore<T.PsString>,
|
|
short: addPronouns({ s: s.short, subject, object, info, displayForm, intransitive, ergative, matrixKey, englishConjugation, negative, sentenceLevel }) as T.ArrayOneOrMore<T.PsString>,
|
|
...s.mini ? {
|
|
mini: addPronouns({ s: s.mini, subject, object, info, displayForm, intransitive, ergative, matrixKey, englishConjugation, negative, sentenceLevel }) as T.ArrayOneOrMore<T.PsString>,
|
|
} : {},
|
|
}
|
|
}
|
|
function makeEnglish(englishBuilder: T.EnglishBuilder, englishConjugation: T.EnglishVerbConjugation): string[] {
|
|
const noObject = (intransitive || info.transitivity === "grammatically transitive" || info.type === "dynamic compound");
|
|
const addRest = (s: string) => (
|
|
`${s}${noObject ? "" : ` ${engObj(object)}`}${englishConjugation.ep ? ` ${englishConjugation.ep}` : ""}`
|
|
);
|
|
return englishBuilder(subject, englishConjugation.ec, negative)
|
|
.map(addRest);
|
|
}
|
|
const firstOrSecondObjectPresent = [0,1,2,3,6,7,8,9].includes(object) && !displayForm.past;
|
|
const nearPronounPossible = (p: T.Person) => [4, 5, 10, 11].includes(p);
|
|
const noPronouns =
|
|
info.transitivity === "grammatically transitive" && displayForm.passive;
|
|
const noObjectPronoun =
|
|
intransitive || info.transitivity === "grammatically transitive" ||
|
|
info.type === "dynamic compound" || info.type === "generative stative compound";
|
|
const transDynCompPast =
|
|
info.transitivity === "transitive" && info.type === "dynamic compound" && displayForm.past;
|
|
const subjectPronoun = (getPersonFromVerbForm(
|
|
pronouns.far[ergative ? "inflected" : "plain"],
|
|
subject,
|
|
) as T.ArrayOneOrMore<T.PsString>)[0];
|
|
const nearSubjectPronoun = (getPersonFromVerbForm(
|
|
pronouns.near[ergative ? "inflected" : "plain"],
|
|
subject,
|
|
) as T.ArrayOneOrMore<T.PsString>)[0];
|
|
const objectPronoun = (getPersonFromVerbForm(
|
|
pronouns.far[firstOrSecondObjectPresent ? "inflected" : "plain"],
|
|
object,
|
|
) as T.ArrayOneOrMore<T.PsString>)[0];
|
|
const nearObjectPronoun = (getPersonFromVerbForm(
|
|
pronouns.near[firstOrSecondObjectPresent ? "inflected" : "plain"],
|
|
object,
|
|
) as T.ArrayOneOrMore<T.PsString>)[0];
|
|
const miniPronoun = (getPersonFromVerbForm(
|
|
pronouns.mini,
|
|
ergative ? subject : object,
|
|
) as T.ArrayOneOrMore<T.PsString>)[0];
|
|
|
|
const prns: Pronouns = noPronouns
|
|
? undefined
|
|
: noObjectPronoun
|
|
? {
|
|
subject: ((sentenceLevel === "hard") && nearPronounPossible(subject)) ? [subjectPronoun, nearSubjectPronoun] : subjectPronoun,
|
|
mini: miniPronoun,
|
|
} : {
|
|
subject: ((sentenceLevel === "hard") && nearPronounPossible(subject)) ? [subjectPronoun, nearSubjectPronoun] : subjectPronoun,
|
|
object: ((sentenceLevel === "hard") && nearPronounPossible(object)) ? [objectPronoun, nearObjectPronoun] : objectPronoun,
|
|
mini: miniPronoun,
|
|
};
|
|
const english = (displayForm.englishBuilder && englishConjugation)
|
|
? makeEnglish(displayForm.englishBuilder, englishConjugation)
|
|
: undefined;
|
|
|
|
function attachPronounsToVariation(ps: T.PsString, prns: Pronouns): T.ArrayOneOrMore<T.PsString> {
|
|
if (!prns) {
|
|
return [ps];
|
|
}
|
|
if (Array.isArray(prns.subject)) {
|
|
return [
|
|
...attachPronounsToVariation(ps, { ...prns, subject: prns.subject[0] }),
|
|
...attachPronounsToVariation(ps, { ...prns, subject: prns.subject[1] }),
|
|
] as T.ArrayOneOrMore<T.PsString>;
|
|
}
|
|
if (Array.isArray(prns.object)) {
|
|
return [
|
|
...attachPronounsToVariation(ps, { ...prns, object: prns.object[0] }),
|
|
...attachPronounsToVariation(ps, { ...prns, object: prns.object[1] }),
|
|
] as T.ArrayOneOrMore<T.PsString>;
|
|
}
|
|
const splitHead = (displayForm.aspect && displayForm.aspect === "perfective")
|
|
? getSplitHead(info[displayForm.past ? "root" : "stem"].perfectiveSplit, matrixKey)
|
|
: undefined;
|
|
const basicForms = (!prns.object)
|
|
// basic form with only one pronoun
|
|
? makeBasicPronounForm(ps, splitHead, displayForm, info, negative, prns.subject)
|
|
: [
|
|
// basic form two full pronouns
|
|
...makeBasicPronounForm(ps, splitHead, displayForm, info, negative, prns.subject, prns.object),
|
|
// basic form one full, one mini pronoun
|
|
...sentenceLevel !== "easy" ? makeBasicPronounForm(
|
|
ps,
|
|
splitHead,
|
|
displayForm,
|
|
info,
|
|
negative,
|
|
ergative ? prns.object : prns.subject,
|
|
prns.mini,
|
|
) : [],
|
|
] as T.ArrayOneOrMore<T.PsString>;
|
|
|
|
const ergativeGrammTrans = (info.transitivity === "grammatically transitive" && ergative);
|
|
const canWorkWithOnlyMini = (prns.object && !displayForm.secondPronounNeeded && formHasVariations(displayForm.form))
|
|
|| transDynCompPast || ergativeGrammTrans;
|
|
return [
|
|
...basicForms,
|
|
...(sentenceLevel !== "easy" && canWorkWithOnlyMini)
|
|
? makeOnlyMiniForm(ps, splitHead, displayForm, info, negative, prns.mini)
|
|
: [],
|
|
].map((ps) => english ? { ...ps, e: english } : ps) as T.ArrayOneOrMore<T.PsString>;
|
|
}
|
|
|
|
// @ts-ignore
|
|
return s.reduce((variations, current) => (
|
|
[...variations, ...uniquePsStringArray(
|
|
attachPronounsToVariation(current, prns)
|
|
)]
|
|
), []) as T.ArrayOneOrMore<T.PsString>;
|
|
}
|
|
|
|
function nuMustGoAfterSplitHead(head: T.PsString) {
|
|
return (
|
|
["و", "وا"].includes(head.p)
|
|
||
|
|
head.p.slice(-1) === " " // compound splits
|
|
||
|
|
head.p.match(`[${pashtoCharacterRange}]* و`)
|
|
);
|
|
}
|
|
|
|
function spaceAfterSplitHead(head: T.PsString) {
|
|
if (nuMustGoAfterSplitHead(head) && head.p.slice(-1) !== " ") {
|
|
return { p: "", f: "-" }
|
|
}
|
|
return { p: " ", f: " " };
|
|
}
|
|
|
|
function makeBasicPronounForm(
|
|
ps: T.PsString,
|
|
splitHead: T.PsString | undefined,
|
|
displayForm: T.DisplayFormForSentence,
|
|
info: T.NonComboVerbInfo,
|
|
negative: boolean,
|
|
firstPronoun: T.PsString,
|
|
secondPronoun?: T.PsString,
|
|
): T.PsString[] {
|
|
if (!negative) {
|
|
return [
|
|
ensureBaAt(
|
|
concatPsString(
|
|
firstPronoun,
|
|
" ",
|
|
secondPronoun ? concatPsString(secondPronoun, " ") : "",
|
|
ps,
|
|
),
|
|
1)
|
|
];
|
|
}
|
|
const objComplement = getObjComplement(info);
|
|
function negativeWOutSplit() {
|
|
if (!displayForm.reorderWithNegative) {
|
|
return [
|
|
ensureBaAt(
|
|
concatPsString(
|
|
firstPronoun,
|
|
" ",
|
|
secondPronoun
|
|
? concatPsString(secondPronoun, " ")
|
|
: objComplement
|
|
? concatPsString(objComplement, " ")
|
|
: "",
|
|
nuParticle,
|
|
" ",
|
|
removeAccents(objComplement ? removeObjComp(objComplement, ps) : ps)
|
|
),
|
|
1),
|
|
];
|
|
}
|
|
const [beginning, end] = splitOffLeapfrogWord(ps);
|
|
return [
|
|
ensureBaAt(
|
|
objComplement ?
|
|
concatPsString(
|
|
firstPronoun,
|
|
" ",
|
|
objComplement,
|
|
" ",
|
|
nuParticle,
|
|
" ",
|
|
end,
|
|
" ",
|
|
removeAccents(removeObjComp(objComplement, beginning)),
|
|
)
|
|
: concatPsString(
|
|
firstPronoun,
|
|
" ",
|
|
secondPronoun ? concatPsString(secondPronoun, " ") : "",
|
|
nuParticle,
|
|
" ",
|
|
end,
|
|
" ",
|
|
removeAccents(beginning),
|
|
),
|
|
1),
|
|
ensureBaAt(
|
|
concatPsString(
|
|
firstPronoun,
|
|
" ",
|
|
secondPronoun ? concatPsString(secondPronoun, " ") : "",
|
|
removeAccents(beginning),
|
|
" ",
|
|
nuParticle,
|
|
" ",
|
|
end,
|
|
),
|
|
1),
|
|
];
|
|
}
|
|
function insertNegInSplit(splitHead: T.PsString) {
|
|
if (!displayForm.reorderWithNegative) {
|
|
return [
|
|
ensureBaAt(
|
|
concatPsString(
|
|
firstPronoun,
|
|
" ",
|
|
secondPronoun ? concatPsString(secondPronoun, " ") : "",
|
|
removeAccents(splitHead),
|
|
spaceAfterSplitHead(splitHead),
|
|
nuParticle,
|
|
" ",
|
|
removeHead(splitHead, ps),
|
|
),
|
|
1),
|
|
];
|
|
}
|
|
const [beginning, end] = splitOffLeapfrogWord(ps);
|
|
return [
|
|
ensureBaAt(
|
|
concatPsString(
|
|
firstPronoun,
|
|
" ",
|
|
secondPronoun ? concatPsString(secondPronoun, " ") : "",
|
|
removeAccents(splitHead),
|
|
spaceAfterSplitHead(splitHead),
|
|
nuParticle,
|
|
" ",
|
|
end,
|
|
" ",
|
|
removeHead(splitHead, beginning),
|
|
),
|
|
1),
|
|
ensureBaAt(
|
|
concatPsString(
|
|
firstPronoun,
|
|
" ",
|
|
secondPronoun ? concatPsString(secondPronoun, " ") : "",
|
|
removeAccents(splitHead),
|
|
spaceAfterSplitHead(splitHead),
|
|
nuParticle,
|
|
" ",
|
|
removeHead(splitHead, beginning),
|
|
" ",
|
|
end,
|
|
),
|
|
1),
|
|
];
|
|
}
|
|
if (splitHead) {
|
|
return nuMustGoAfterSplitHead(splitHead) ? insertNegInSplit(splitHead) : [
|
|
...insertNegInSplit(splitHead),
|
|
...negativeWOutSplit(),
|
|
]
|
|
}
|
|
return negativeWOutSplit();
|
|
}
|
|
|
|
function makeOnlyMiniForm(
|
|
ps: T.PsString,
|
|
splitHead: T.PsString | undefined,
|
|
displayForm: T.DisplayFormForSentence,
|
|
info: T.NonComboVerbInfo,
|
|
negative: boolean,
|
|
mini: T.PsString,
|
|
): T.PsString[] {
|
|
const objComplement = getObjComplement(info);
|
|
function reorderedNegativeAfterSplitHead(splitHead: T.PsString) {
|
|
const [beginning, end] = splitOffLeapfrogWord(ps);
|
|
return ensureBaAt(
|
|
objComplement ?
|
|
concatPsString(
|
|
objComplement,
|
|
" ",
|
|
mini,
|
|
" ",
|
|
removeAccents(removeObjComp(objComplement, splitHead)),
|
|
spaceAfterSplitHead(splitHead),
|
|
nuParticle,
|
|
" ",
|
|
end,
|
|
" ",
|
|
removeHead(splitHead, beginning),
|
|
)
|
|
: concatPsString(
|
|
removeAccents(splitHead),
|
|
spaceAfterSplitHead(splitHead),
|
|
mini,
|
|
" ",
|
|
nuParticle,
|
|
" ",
|
|
end,
|
|
" ",
|
|
removeHead(splitHead, beginning),
|
|
),
|
|
1)
|
|
}
|
|
if (splitHead) {
|
|
// only mini pronoun with split
|
|
if (!displayForm.reorderWithNegative || !negative) {
|
|
const safeSplitHead = removeObjComp(objComplement, splitHead);
|
|
return [ensureBaAt(
|
|
concatPsString(
|
|
objComplement ? concatPsString(objComplement, " ", mini, " ") : "",
|
|
negative ? removeAccents(safeSplitHead) : safeSplitHead,
|
|
spaceAfterSplitHead(safeSplitHead),
|
|
!objComplement ? concatPsString(mini, " ") : "",
|
|
negative ? concatPsString(nuParticle, " ") : "",
|
|
removeHead(splitHead, ps)
|
|
),
|
|
1)];
|
|
}
|
|
// if (!nuMustGoAfterSplitHead(splitHead)) {
|
|
// TODO: IS THIS A SEPERATELY NECESSARY THING FOR VERBS LIKE
|
|
// PREXODUL -- LIKE COULD YOU ALSO DO A VERSION WHERE THE SPLIT ISN'T USED
|
|
// return [reorderedNegativeAfterSplitHead(splitHead)];
|
|
// }
|
|
return [reorderedNegativeAfterSplitHead(splitHead)];
|
|
}
|
|
// only mini without split
|
|
const [beginning, end] = splitOffLeapfrogWord(ps);
|
|
if (!displayForm.reorderWithNegative || !negative) {
|
|
if (objComplement) {
|
|
return [
|
|
concatPsString(
|
|
objComplement,
|
|
psStringContains(ps, concatPsString(baParticle, " ")) ? concatPsString(" ", baParticle, " ") : " ",
|
|
concatPsString(mini, " "),
|
|
negative ? concatPsString(" ", nuParticle, " ") : "",
|
|
removeObjComp(objComplement, psRemove(ps, concatPsString(baParticle, " "))),
|
|
)
|
|
];
|
|
}
|
|
return [
|
|
concatPsString(
|
|
psRemove(beginning, concatPsString(baParticle, " ")),
|
|
" ",
|
|
psStringContains(beginning, concatPsString(baParticle, " ")) ? concatPsString(baParticle, " ") : "",
|
|
negative ? concatPsString(" ", nuParticle, " ") : " ",
|
|
(beginning.p || negative) ? concatPsString(mini, " ") : "",
|
|
end,
|
|
(beginning.p || negative) ? "" : concatPsString(" ", mini),
|
|
),
|
|
];
|
|
}
|
|
if (objComplement) {
|
|
return [
|
|
ensureBaAt(
|
|
concatPsString(
|
|
objComplement,
|
|
" ",
|
|
mini,
|
|
" ",
|
|
nuParticle,
|
|
" ",
|
|
end,
|
|
" ",
|
|
removeObjComp(objComplement, beginning),
|
|
),
|
|
1),
|
|
ensureBaAt(
|
|
concatPsString(
|
|
objComplement,
|
|
" ",
|
|
mini,
|
|
" ",
|
|
removeObjComp(objComplement, beginning),
|
|
" ",
|
|
nuParticle,
|
|
" ",
|
|
end,
|
|
),
|
|
1),
|
|
]
|
|
}
|
|
return [
|
|
ensureBaAt(
|
|
concatPsString(
|
|
beginning,
|
|
" ",
|
|
mini,
|
|
" ",
|
|
nuParticle,
|
|
" ",
|
|
end,
|
|
),
|
|
1),
|
|
ensureBaAt(
|
|
concatPsString(
|
|
nuParticle,
|
|
" ",
|
|
end,
|
|
" ",
|
|
mini,
|
|
" ",
|
|
beginning,
|
|
),
|
|
1),
|
|
];
|
|
}
|
|
|
|
function getObjComplement(info: T.NonComboVerbInfo): T.PsString | undefined {
|
|
return info.type === "dynamic compound" ?
|
|
(info.objComplement.plural ? info.objComplement.plural : info.objComplement.entry) :
|
|
undefined;
|
|
}
|
|
|
|
function engObj(s: T.Person): string {
|
|
return (s === T.Person.FirstSingMale || s === T.Person.FirstSingFemale)
|
|
? "me"
|
|
: (s === T.Person.FirstPlurMale || s === T.Person.FirstPlurFemale)
|
|
? "us"
|
|
: (s === T.Person.SecondSingMale || s === T.Person.SecondSingFemale)
|
|
? "you"
|
|
: (s === T.Person.SecondPlurMale || s === T.Person.SecondPlurFemale)
|
|
? "you (pl.)"
|
|
: (s === T.Person.ThirdSingMale)
|
|
? "him/it"
|
|
: (s === T.Person.ThirdSingFemale)
|
|
? "her/it"
|
|
: (s === T.Person.ThirdPlurMale)
|
|
? "them"
|
|
: "them (f.)";
|
|
}
|