This commit is contained in:
adueck 2023-04-07 15:48:33 +04:00
parent 1dbe0a42f4
commit 305fb545b9
10 changed files with 420 additions and 508 deletions

View File

@ -153,7 +153,7 @@ function VPBuilderDemo({ opts }: {
person: testPerson.person, person: testPerson.person,
voice: testVoice, voice: testVoice,
}) : undefined; }) : undefined;
const rs = v ? getAllRs(v.verb as T.VerbEntry) : undefined // const rs = v ? getAllRs(v.verb as T.VerbEntry) : undefined
return <div className="mt-4"> return <div className="mt-4">
<div className="d-block mx-auto card" style={{ maxWidth: "700px", background: "var(--closer)"}}> <div className="d-block mx-auto card" style={{ maxWidth: "700px", background: "var(--closer)"}}>
<div className="card-body"> <div className="card-body">
@ -252,9 +252,12 @@ function VPBuilderDemo({ opts }: {
role="subject" role="subject"
opts={opts} opts={opts}
/> />
<pre> {/* <details>
{JSON.stringify(rs, null, " ")} <summary>Roots and Stems</summary>
</pre> <pre>
{JSON.stringify(rs, null, " ")}
</pre>
</details> */}
<pre> <pre>
{JSON.stringify(rv, null, " ")} {JSON.stringify(rv, null, " ")}
</pre> </pre>

View File

@ -104,7 +104,7 @@ const accentReplacer = [
{ vowel: "U", accented: "Ú" }, { vowel: "U", accented: "Ú" },
]; ];
function accentSyllable(s: string): string { export function accentSyllable(s: string): string {
return s.replace(/a|e|i|o|u|U/, (match) => { return s.replace(/a|e|i|o|u|U/, (match) => {
const r = accentReplacer.find((x) => x.vowel === match); const r = accentReplacer.find((x) => x.vowel === match);
/* istanbul ignore next */ /* istanbul ignore next */
@ -112,6 +112,14 @@ function accentSyllable(s: string): string {
}); });
} }
export function accentPsSyllable(ps: T.PsString): T.PsString {
return {
p: ps.p,
f: accentSyllable(ps.f),
};
}
export function removeAccentsWLength(s: T.SingleOrLengthOpts<T.PsString[]>): T.SingleOrLengthOpts<T.PsString[]> { export function removeAccentsWLength(s: T.SingleOrLengthOpts<T.PsString[]>): T.SingleOrLengthOpts<T.PsString[]> {
if ("long" in s) { if ("long" in s) {
return { return {

View File

@ -0,0 +1,24 @@
import * as T from "../../../types";
import { accentOnFront } from "../accent-helpers";
import { countSyllables } from "../accent-helpers";
import { concatPsString, trimOffPs } from "../p-text-helpers";
export function inflectPattern1(ps: T.PsString, { gender, number }: T.GenderNumber): T.PsString[] {
if (gender === "masc") {
return [ps];
}
const inflected = concatPsString(ps, number === "singular" ? { p: "ه", f: "a" } : { p: "ې", f: "e" });
if (countSyllables(inflected) === 2) {
return [accentOnFront(inflected)];
}
return [inflected];
}
export function inflectPattern3(ps: T.PsString, { gender, number }: T.GenderNumber): T.PsString[] {
if (gender === "masc") {
return number === "singular"
? [ps]
: [concatPsString(trimOffPs(ps, 1, 2), { p: "ي", f: "ee" })];
}
return [concatPsString(trimOffPs(ps, 1, 2), { p: "ې", f: "e" })];
}

View File

@ -1,14 +1,11 @@
import * as T from "../../../types"; import * as T from "../../../types";
import { import {
functionOnOptLengths,
getPersonInflectionsKey,
getVerbBlockPosFromPerson, getVerbBlockPosFromPerson,
personGender, personGender,
personIsPlural,
personNumber, personNumber,
} from "../misc-helpers"; } from "../misc-helpers";
import { import {
yulEndingInfinitive, trimOffPs,
} from "../p-text-helpers"; } from "../p-text-helpers";
import { import {
concatPsString, concatPsString,
@ -21,19 +18,14 @@ import {
} from "../grammar-units"; } from "../grammar-units";
import { isKawulVerb, isAbilityTense, isPerfectTense, isTlulVerb } from "../type-predicates"; import { isKawulVerb, isAbilityTense, isPerfectTense, isTlulVerb } from "../type-predicates";
import { tenseHasBa } from "../phrase-building/vp-tools"; import { tenseHasBa } from "../phrase-building/vp-tools";
import { inflectYey } from "../pashto-inflector";
import {
getVerbInfo,
} from "../verb-info";
import { isPastTense } from "../phrase-building/vp-tools"; import { isPastTense } from "../phrase-building/vp-tools";
import { makePsString, removeFVarients } from "../accent-and-ps-utils"; import { makePsString, removeFVarients } from "../accent-and-ps-utils";
import { pashtoConsonants } from "../pashto-consonants"; import { pashtoConsonants } from "../pashto-consonants";
import { accentOnNFromEnd, removeAccents } from "../accent-helpers"; import { accentPsSyllable } from "../accent-helpers";
import { getRootStem as newGetRootStem } from "./roots-and-stems"; import { getRootStem } from "./roots-and-stems";
const kedulStatVerb: T.VerbEntry = { // For the chart display of the results: base the length thing on the VBE at the end, if there are other
entry: {"ts":1581086654898,"i":11100,"p":"کېدل","f":"kedul","g":"kedul","e":"to become _____","r":2,"c":"v. intrans.","ssp":"ش","ssf":"sh","prp":"شول","prf":"shwul","pprtp":"شوی","pprtf":"shúwey","noOo":true,"ec":"become"} as T.VerbDictionaryEntry, // length variations earlier in the blocks, flatten those into the variations
};
// TODO: Amazingly, the basic formula with the roots and stems from the basic verbs // TODO: Amazingly, the basic formula with the roots and stems from the basic verbs
// works perfectly with stative compounds as well! // works perfectly with stative compounds as well!
@ -56,302 +48,126 @@ export function renderVerb({ verb, tense, person, voice }: {
voice: T.Voice, voice: T.Voice,
}): { }): {
hasBa: boolean, hasBa: boolean,
verbBlocks: T.VB[], vbs: T.VerbRenderedOutput,
} { } {
// TODO: check for transitivity with passive voice ?? // TODO: check for transitivity with passive voice ??
const hasBa = tenseHasBa(tense);
if (isPerfectTense(tense)) { if (isPerfectTense(tense)) {
return getPerfectVBs({ verb, tense, person, voice }); throw new Error("not implemented yet");
} }
const hasBa = tenseHasBa(tense);
const isPast = isPastTense(tense); const isPast = isPastTense(tense);
const aspect = getAspect(tense); const aspect = getAspect(tense);
const isAbility = isAbilityTense(tense); const isAbility = isAbilityTense(tense);
const noPerfective = isAbility && (voice === "passive" || isTlulVerb(verb) || isKedul(verb)); const genderNumber = {
// console.log(newGetRootStem({
// verb,
// part: {
// rs: isPast ? "root" : "stem",
// aspect,
// },
// type: "basic",
// person: undefined,
// }));
const { perfectiveHead, rootStem } = getRootStem({
verb, aspect, isPast, isAbility, person, voice, noPerfective,
});
const verbBlocks: T.VB[] = isAbility
? getAbilityVerbBlocks({ isPast, person, root: rootStem, aspect, voice, noPerfective })
: voice === "passive"
? getPassiveVerbBlocks({ root: rootStem, tense, person })
: getBasicVerbBlock({
rootStem, person, isPast, verb, aspect,
});
return {
hasBa,
verbBlocks: [
...(!noPerfective && perfectiveHead)
? [perfectiveHead] : [],
...verbBlocks,
],
};
}
function getBasicVerbBlock({ rootStem, person, isPast, aspect, verb }: {
rootStem: T.SingleOrLengthOpts<T.PsString>,
person: T.Person,
isPast: boolean,
aspect: T.Aspect,
verb: T.VerbEntry,
}): [T.VA] {
const ending = getEnding(person, isPast);
return [{
type: "VA",
ps: addEnding({
rootStem, ending, person, isPast, verb, aspect,
}),
person,
}];
}
function getPassiveVerbBlocks({ root, tense, person }: {
root: T.SingleOrLengthOpts<T.PsString>,
tense: T.VerbTense,
person: T.Person,
}): T.VB[] {
/* istanbul ignore next */
if (!("long" in root)) {
throw new Error("should have length versions in roots for passive");
}
const { verbBlocks: [auxVerb] } = renderVerb({
// TODO: "kedulStat" verb argument with overload that removes the "as" usage
verb: kedulStatVerb,
tense,
person,
voice: "active",
}) as { hasBa: boolean, verbBlocks: [T.VA] };
return [
weld(
{
type: "VI",
ps: [root.long],
},
auxVerb,
),
];
}
function getAbilityVerbBlocks({ isPast, person, root, aspect, voice, noPerfective }: {
isPast: boolean,
person: T.Person,
root: T.SingleOrLengthOpts<T.PsString>,
aspect: T.Aspect,
voice: T.Voice,
noPerfective: boolean,
}): T.VB[] {
/* istanbul ignore next */
if (!("long" in root)) {
throw new Error("should have length versions in roots for passive");
}
const shBlock = getAbilityShPart(isPast, person);
const adjustedAspect = noPerfective ? "imperfective" : aspect;
if (voice === "passive") {
return [
weld(
{
type: "VI",
ps: [root.long],
},
{
type: "VI",
ps: addAbilityTailsToRs({
long: { p: "کېدل", f: "kedúl" },
short: { p: "کېد", f: "ked" },
}, adjustedAspect),
},
),
shBlock,
];
}
// TODO: this is redundant, we did it in another part of the program?
const verbBlock: T.VI = {
type: "VI",
ps: addAbilityTailsToRs(root, adjustedAspect),
};
return [verbBlock, shBlock];
}
function getAbilityShPart(isPast: boolean, person: T.Person): T.VA {
// TODO: optimized shortcut version of this
const { verbBlocks: [shBlock] } = renderVerb({
verb: kedulStatVerb,
tense: isPast ? "perfectivePast" : "subjunctiveVerb",
person,
voice: "active",
}) as {
hasBa: boolean,
verbBlocks: [T.VA],
};
return {
type: "VA",
ps: shBlock.ps,
person,
};
}
function getPerfectVBs({ verb, tense, person, voice }: {
verb: T.VerbEntry,
tense: T.PerfectTense,
person: T.Person,
voice: T.Voice,
}): { hasBa: boolean, verbBlocks: T.VB[] } {
const hasBa = tenseHasBa(tense);
const vInfo = getVerbInfo(verb.entry, verb.complement) as T.SimpleVerbInfo;
if (voice === "passive") {
const [pt, eq] = getKedulStatPerfect(person, tense);
const passiveRoot: T.VI = {
type: "VI",
ps: [fromPersInfls(vInfo.root.imperfective, person).long],
};
const welded: T.Welded = weld(passiveRoot, pt);
return {
hasBa,
verbBlocks: [welded, eq],
};
}
const equative = equativeEndings[perfectTenseToEquative(tense)];
const [row, col] = getVerbBlockPosFromPerson(person);
const equativeBlock: T.EQ = {
type: "EQ",
person,
ps: "long" in equative ? {
long: equative.long[row][col],
short: equative.short[row][col],
} : equative[row][col],
}
const participleBlock: T.PT = {
type: "PT",
gender: personGender(person), gender: personGender(person),
number: personNumber(person), number: personNumber(person),
ps: chooseParticipleInflection(inflectYey(fromPersInfls(vInfo.participle.past, person)), person) };
} // #1 get the appropriate root / stem
console.log({ isAbility });
const [vHead, rest] = getRootStem({
verb,
part: {
rs: isPast ? "root" : "stem",
aspect,
},
type: isAbility ? "ability" : "basic",
genderNumber,
});
// #2 add the verb ending to it
const ending = getEnding(person, isPast);
return { return {
hasBa, hasBa,
verbBlocks: [participleBlock, equativeBlock], vbs: [
vHead,
addEnding({
rs: rest,
ending,
verb,
person,
pastThird: isPast && person === T.Person.ThirdSingMale,
aspect,
}),
],
}; };
} }
// const equative = equativeEndings[perfectTenseToEquative(tense)];
// const [row, col] = getVerbBlockPosFromPerson(person);
// const equativeBlock: T.EQ = {
// type: "EQ",
// person,
// ps: "long" in equative ? {
// long: equative.long[row][col],
// short: equative.short[row][col],
// } : equative[row][col],
// }
function weld(left: T.VI, right: T.VA | T.PT | T.VI): T.Welded { function addEnding({ verb, rs, ending, person, pastThird, aspect }: {
return { rs: [T.VB, T.VBA] | [T.VBA],
type: "welded",
left: {
...left,
ps: functionOnOptLengths(left.ps, removeAccents),
},
right,
};
}
function getRootStem({ verb, aspect, isPast, isAbility, voice, person, noPerfective }: {
verb: T.VerbEntry,
aspect: T.Aspect,
isPast: boolean,
isAbility: boolean,
person: T.Person,
voice: T.Voice,
noPerfective: boolean,
}): {
perfectiveHead: undefined | T.PH,
rootStem: T.SingleOrLengthOpts<T.PsString>,
} {
const vInfo = getVerbInfo(verb.entry, verb.complement) as T.SimpleVerbInfo;
const rs = vInfo[(isPast || isAbility || voice === "passive") ? "root" : "stem"];
if (noPerfective) {
// exception with tlul verbs for ability stems
return {
perfectiveHead: undefined,
rootStem: fromPersInfls(rs.imperfective, person),
};
}
if (aspect === "perfective" && rs.perfectiveSplit) {
return extractPerfectiveSplit(rs.perfectiveSplit);
}
return {
perfectiveHead: undefined,
// because the persInfs only happen with stative compound verbs,j
// which we are handling differently now
rootStem: fromPersInfls(rs[aspect], person),
}
function extractPerfectiveSplit(splitInfo: T.SplitInfo): ReturnType<typeof getRootStem> {
// this is just for tlul and Daredul ?
const si = fromPersInfls(splitInfo, person);
if ("long" in si) {
return {
perfectiveHead: {
type: "PH",
ps: fromPersInfls(si.long[0], person),
},
rootStem: {
long: si.long[1],
short: si.short[1],
}
}
} else {
return {
perfectiveHead: {
type: "PH",
// should only need this for tlul and Daaredul?
ps: fromPersInfls(si[0], person),
},
rootStem: si[1],
};
}
}
}
function addEnding({ rootStem, ending, person, isPast, verb, aspect }:{
rootStem: T.SingleOrLengthOpts<T.PsString>,
ending: T.SingleOrLengthOpts<T.PsString[]>, ending: T.SingleOrLengthOpts<T.PsString[]>,
person: T.Person, person: T.Person,
isPast: boolean,
verb: T.VerbEntry, verb: T.VerbEntry,
pastThird: boolean,
aspect: T.Aspect, aspect: T.Aspect,
}): T.SingleOrLengthOpts<T.PsString[]> { }): [T.VB, T.VBE] | [T.VBE] {
// TODO: no need for useless abbreviation now return rs.length === 2
const rs = rootStem; ? [rs[0], addEnd(rs[1], ending)]
const end = ending; : [addEnd(rs[0], ending)];
const idiosyncratic3rdPast = isPast && person === T.Person.ThirdSingMale; function addEnd(vba: T.VBA, ending: T.SingleOrLengthOpts<T.PsString[]>): T.VBE {
const safeEndAdd = (rs: T.PsString) => (ending: T.PsString): T.PsString => ( if (vba.type === "welded") {
(ending.p === "ل" && rs.p.slice(-1) === "ل") return {
? rs ...vba,
: concatPsString(rs, ending) right: addToVBBasicEnd(vba.right, ending),
); person,
if ("long" in rs) { };
const endLong = getLength(end, "long"); }
const endShort = getLength(end, "short");
const shortForm = idiosyncratic3rdPast
? ensure3rdPast(endShort, rs.short, verb, aspect)
: endShort.map(e => concatPsString(rs.short, e));
// TODO: this is a bit hacky
const rsLong = rs.long.f === "tlul"
? { p: "تلل", f: "tlúl" }
: rs.long;
return { return {
long: endLong.map(safeEndAdd(rsLong)), ...addToVBBasicEnd(vba, ending),
short: aspect === "imperfective" person,
? applyImperfectiveShortAccent(shortForm, yulEndingInfinitive(removeFVarients(verb.entry))) }
: shortForm, }
function addToVBBasicEnd(vb: T.VBBasic, end: T.SingleOrLengthOpts<T.PsString[]>): T.VBBasic {
if ("long" in vb.ps) {
const endLong = getLength(end, "long");
const endShort = getLength(end, "short");
// TODO: this is hacky
const rsLong: T.PsString[] = vb.ps.long[0].f === "tlul"
? [{ p: "تلل", f: "tlúl" }]
: vb.ps.long;
return {
...vb,
ps: {
long: verbEndingConcat(rsLong, endLong),
short: pastThird
? ensure3rdPast(endShort, vb.ps.short, verb, aspect)
: verbEndingConcat(vb.ps.short, endShort),
},
};
}
/* istanbul ignore next */
if ("long" in end) {
throw new Error("should not be using verb stems with long and short endings");
}
return {
...vb,
ps: verbEndingConcat(vb.ps, end),
}; };
} }
/* istanbul ignore next */ }
if ("long" in end) {
throw new Error("should not be using verb stems with long and short endings"); function verbEndingConcat(ps: T.PsString[], end: T.PsString[]): T.PsString[] {
} return ps.flatMap(v => (
return end.map((e) => concatPsString(rs, e)); end.map(e => {
if (v.f.charAt(v.f.length-1) === "X") {
return concatPsString(trimOffPs(v, 0, 1), accentPsSyllable(e))
}
if (e.p === "ل" && ["ul", "úl"].includes(v.f.slice(-2))) {
return v;
}
return concatPsString(v, e);
})
));
} }
function getEnding(person: T.Person, isPast: boolean) { function getEnding(person: T.Person, isPast: boolean) {
@ -362,53 +178,12 @@ function getEnding(person: T.Person, isPast: boolean) {
} : presentEndings[row][col]; } : presentEndings[row][col];
} }
function chooseParticipleInflection(pinf: T.SingleOrLengthOpts<T.UnisexInflections>, p: T.Person): T.SingleOrLengthOpts<T.PsString[]> { // TODO: THIS IS PROBABLY SKEEEETCH
if ("long" in pinf) { function ensure3rdPast(ending: T.PsString[], rs: T.PsString[], verb: T.VerbEntry, aspect: T.Aspect): T.PsString[] {
return { if (isKedul(verb) && rs[0].p === "شو") {
short: chooseParticipleInflection(pinf.short, p) as T.PsString[],
long: chooseParticipleInflection(pinf.long, p) as T.PsString[],
};
}
const gender = personGender(p);
const infNum = personIsPlural(p) ? 1 : 0;
return pinf[gender][infNum];
}
function addAbilityTailsToRs(rs: T.LengthOptions<T.PsString>, aspect: T.Aspect): T.SingleOrLengthOpts<T.PsString[]> {
const tails: T.PsString[] = [
{ p: "ی", f: "ey" },
{ p: "ای", f: "aay" },
];
const accentedTails: T.PsString[] = [
{ p: "ی", f: "éy" },
{ p: "ای", f: "áay" },
];
// for single syllable long verb stems like tlul - ensure the accent
const psLong = (aspect === "perfective")
? removeAccents(rs.long)
: ensureAccentLongStem(rs.long);
return {
long: tails.map(t => concatPsString(psLong, t)),
short: (aspect === "perfective" ? tails : accentedTails)
.map(t => concatPsString(rs.short, t)),
};
}
function applyImperfectiveShortAccent(form: T.PsString[], yulEnding: boolean): T.PsString[] {
// TODO: remove this hack - get proper working thing for accentOnNFromEnd not adding accent on 1 syllable words
if (form[0].f === "tu") {
return form;
}
return form.map(f => {
return accentOnNFromEnd(f, yulEnding ? 1 : 0);
});
}
function ensure3rdPast(ending: T.PsString[], rs: T.PsString, verb: T.VerbEntry, aspect: T.Aspect): T.PsString[] {
if (isKedul(verb) && rs.p === "شو") {
return [{ p: "شو", f: "sho" }]; return [{ p: "شو", f: "sho" }];
} }
if (isKawulVerb(verb) && rs.p === "کړ") { if (isKawulVerb(verb) && rs[0].p === "کړ") {
return [ return [
{ p: "کړ", f: "kuR" }, { p: "کړ", f: "kuR" },
{ p: "کړه", f: "kRu" }, { p: "کړه", f: "kRu" },
@ -420,12 +195,12 @@ function ensure3rdPast(ending: T.PsString[], rs: T.PsString, verb: T.VerbEntry,
return [{ p: "غی", f: "ghey" }]; return [{ p: "غی", f: "ghey" }];
} }
const specialTuShort: T.PsString = { const specialTuShort: T.PsString = {
p: rs.p.slice(0, -1) + "ه", p: rs[0].p.slice(0, -1) + "ه",
f: rs.f.slice(0, -1) + (rs.f === "tl" ? "u" : "ú"), f: rs[0].f.slice(0, -1) + (rs[0].f === "tl" ? "u" : "ú"),
}; };
return [ return [
specialTuShort, specialTuShort,
concatPsString(rs, { p: "و", f: "o" }), concatPsString(rs[0], { p: "و", f: "o" }),
]; ];
} }
if (verb.entry.tppp && verb.entry.tppf) { if (verb.entry.tppp && verb.entry.tppf) {
@ -448,15 +223,15 @@ function ensure3rdPast(ending: T.PsString[], rs: T.PsString, verb: T.VerbEntry,
); );
// TODO: check about verbs like tawul (if they exist) // TODO: check about verbs like tawul (if they exist)
if (endsInAwul) { if (endsInAwul) {
const base = { p: rs.p.slice(0, -1), f: rs.f.slice(0, -2) }; const base = { p: rs[0].p.slice(0, -1), f: rs[0].f.slice(0, -2) };
return [concatPsString(base, { p: "اوه", f: "aawu" })]; return [concatPsString(base, { p: "اوه", f: "aawu" })];
} }
const endsInDental = ["د", "ت"].includes(rs.p.slice(-1)); const endsInDental = ["د", "ت"].includes(rs[0].p.slice(-1));
// short endings like ورسېد // short endings like ورسېد
return (endsInDental ? [ return (endsInDental ? [
"", "",
...ending, ...ending,
] : ending).map(e => concatPsString(rs, e)); ] : ending).map(e => concatPsString(rs[0], e));
} }
function getAspect(tense: T.VerbTense | T.AbilityTense): T.Aspect { function getAspect(tense: T.VerbTense | T.AbilityTense): T.Aspect {
@ -472,34 +247,6 @@ function isKedul(v: T.VerbEntry): boolean {
return v.entry.p === "کېدل"; return v.entry.p === "کېدل";
} }
function fromPersInfls<U extends object>(x: T.OptionalPersonInflections<U>, person: T.Person): U {
if ("mascSing" in x) {
return x[getPersonInflectionsKey(person)];
} else {
return x;
}
}
function ensureAccentLongStem(ps: T.PsString): T.PsString {
if (ps.f.charAt(ps.f.length - 2) === "ú") {
return ps;
}
return {
p: ps.p,
f: ps.f.slice(0, -2) + "úl",
};
}
function getKedulStatPerfect(person: T.Person, tense: T.PerfectTense): [T.PT, T.EQ] {
const { verbBlocks: [pt, eq] } = renderVerb({
verb: kedulStatVerb,
tense,
person,
voice: "active",
}) as { hasBa: true, verbBlocks: [T.PT, T.EQ] };
return [pt, eq];
}
function perfectTenseToEquative(t: T.PerfectTense): keyof typeof equativeEndings { function perfectTenseToEquative(t: T.PerfectTense): keyof typeof equativeEndings {
return t === "presentPerfect" return t === "presentPerfect"
? "present" ? "present"

View File

@ -29,7 +29,7 @@ const ooPh: T.PH = {
ps: { p: "و", f: "óo" }, ps: { p: "و", f: "óo" },
}; };
type TestGroup = { type RootsAndStemsTestGroup = {
verb: T.VerbEntry, verb: T.VerbEntry,
result: Record<"stem" | "root", { result: Record<"stem" | "root", {
imperfective: T.RootStemOutput, imperfective: T.RootStemOutput,
@ -37,9 +37,9 @@ type TestGroup = {
}> }>
}[]; }[];
const tests: { const rootsAndStemsTests: {
title: string, title: string,
testGroup: TestGroup, testGroup: RootsAndStemsTestGroup,
}[] = }[] =
[ [
{ {
@ -615,7 +615,7 @@ const tests: {
} }
] ]
} }
}, },
}, },
], ],
}, },
@ -824,7 +824,7 @@ const tests: {
} }
] ]
} }
}, },
}, },
{ {
verb: kedulDyn, verb: kedulDyn,
@ -901,14 +901,20 @@ const tests: {
verb: kawulStat, verb: kawulStat,
result: { result: {
"stem": { "stem": {
"perfective": [ "perfective": [{
[ long: [
{ {
"p": "کړ", "p": "کړ",
"f": "kR" "f": "kR"
} }
] ],
], short: [
{
p: "ک",
f: "k",
},
],
}],
"imperfective": [ "imperfective": [
[ [
{ {
@ -932,7 +938,13 @@ const tests: {
"p": "کړ", "p": "کړ",
"f": "kR" "f": "kR"
} }
] ],
"mini": [
{
"p": "ک",
"f": "k",
},
],
} }
], ],
"imperfective": [ "imperfective": [
@ -966,12 +978,17 @@ const tests: {
"f": "óo", "f": "óo",
} }
}, },
[ {
{ long: [
"p": "کړ", {
"f": "kR" "p": "کړ",
} "f": "kR"
] },
],
short: [
{ p: "ک", f: "k" },
],
},
], ],
"imperfective": [ "imperfective": [
[ [
@ -1003,8 +1020,11 @@ const tests: {
"p": "کړ", "p": "کړ",
"f": "kR" "f": "kR"
} }
] ],
} "mini": [
{ p: "ک", f: "k" },
],
},
], ],
"imperfective": [ "imperfective": [
{ {
@ -1029,7 +1049,7 @@ const tests: {
} }
]; ];
tests.forEach(({ title, testGroup }) => { rootsAndStemsTests.forEach(({ title, testGroup }) => {
test(title, () => { test(title, () => {
testGroup.forEach(({ verb, result }) => { testGroup.forEach(({ verb, result }) => {
expect(getAllRs(verb)).toEqual(result); expect(getAllRs(verb)).toEqual(result);

View File

@ -7,47 +7,83 @@
*/ */
import { import {
concatPsString, concatPsString, trimOffPs,
} from "../p-text-helpers"; } from "../p-text-helpers";
import * as T from "../../../types"; import * as T from "../../../types";
import { makePsString, removeFVarientsFromVerb } from "../accent-and-ps-utils"; import { makePsString, removeFVarientsFromVerb } from "../accent-and-ps-utils";
import { accentOnNFromEnd, removeAccents } from "../accent-helpers"; import { accentOnNFromEnd, accentSyllable, removeAccents } from "../accent-helpers";
import { assertNever } from "../assert-never"; import { isKawulVerb } from "../type-predicates";
import { inflectPattern1 } from "./new-inflectors";
export function getRootStem({ verb, part, type, person }: { const shwulVB: T.VBBasic = {
type: "VB",
ps: {
long: [{ p: "شول", f: "shwul" }],
short: [{ p: "شو", f: "shw" }],
},
}
// start basic inflection functions for pattern 1 and pattern ey things
// to be used by inflecting لاړ and participles
export function getRootStem({ verb, part, type, genderNumber }: {
verb: T.VerbEntry, verb: T.VerbEntry,
part: { part: {
rs: "root" | "stem", rs: "root" | "stem",
aspect: T.Aspect, aspect: T.Aspect,
} | { } | "pastPart",
participle: "present" | "past",
},
type: "basic" | "ability" | "passive", type: "basic" | "ability" | "passive",
person: { genderNumber: {
gender: T.Gender, gender: T.Gender,
number: T.NounNumber, number: T.NounNumber,
} | undefined, },
}): T.RootStemOutput { }): T.RootsStemsOutput {
console.log({ type });
const v = removeFVarientsFromVerb(verb); const v = removeFVarientsFromVerb(verb);
if ("participle" in part) { if (part === "pastPart") {
return []; throw new Error("not implemented yet");
} }
console.log({ part });
return part.rs === "stem" return part.rs === "stem"
? getStem(v, part.aspect) ? part.aspect === "imperfective"
: getRoot(v, part.aspect); ? getImperfectiveStem(v)
: getPerfectiveStem(v, genderNumber)
: part.aspect === "imperfective"
? getImperfectiveRoot(v, type)
: getPerfectiveRoot(v);
} }
function getRoot(verb: T.VerbEntryNoFVars, aspect: T.Aspect): T.RootStemOutput { function getImperfectiveRoot(verb: T.VerbEntryNoFVars, type: "basic" | "ability" | "passive"): T.RootsStemsOutput {
if (aspect === "imperfective") { // if (type === "ability") {
const infinitive = accentOnNFromEnd(makePsString(verb.entry.p, verb.entry.f), 0); // console.log("in ability");
return [ // const basic = getImperfectiveRoot(verb, "basic") as [[T.VHead] | [], [T.VBA]];
// return [
// basic[0],
// [
// {
// type: "VB",
// [1],
// shwulVB,
// ],
// ];
// }
const infinitive = accentOnNFromEnd(makePsString(verb.entry.p, verb.entry.f), 0);
return [
[],
[
{ {
long: [infinitive], type: "VB",
short: [addTrailingAccent(removeL(infinitive))], ps: {
long: [infinitive],
short: [addTrailingAccent(removeL(infinitive))],
},
}, },
]; ],
} ];
// aspect === "perfective" }
function getPerfectiveRoot(verb: T.VerbEntryNoFVars): T.RootsStemsOutput {
const base = removeAccents( const base = removeAccents(
(verb.entry.prp && verb.entry.prf) (verb.entry.prp && verb.entry.prf)
? makePsString(verb.entry.prp, verb.entry.prf) ? makePsString(verb.entry.prp, verb.entry.prf)
@ -55,66 +91,108 @@ function getRoot(verb: T.VerbEntryNoFVars, aspect: T.Aspect): T.RootStemOutput {
); );
const [perfectiveHead, rest] = getPerfectiveHead(base, verb); const [perfectiveHead, rest] = getPerfectiveHead(base, verb);
return [ return [
...perfectiveHead ? [perfectiveHead] : [], perfectiveHead ? [perfectiveHead] : [],
{ [
long: [rest], {
short: [removeL(rest)], type: "VB",
}, ps: {
long: [rest],
short: [removeL(rest)],
...isKawulVerb(verb) ? {
mini: [{ p: "ک", f: "k" }],
} : {},
},
},
],
]; ];
} }
function getStem(verb: T.VerbEntryNoFVars, aspect: T.Aspect): T.RootStemOutput { function getImperfectiveStem(verb: T.VerbEntryNoFVars): T.RootsStemsOutput {
if (aspect === "imperfective") { if (verb.entry.psp && verb.entry.psf) {
if (verb.entry.psp && verb.entry.psf) {
return [[makePsString(verb.entry.psp, verb.entry.psf)]];
}
const rawBase = removeL(makePsString(verb.entry.p, verb.entry.f));
if (verb.entry.c?.includes("intrans.") && rawBase.p.endsWith("ېد")) {
const long: T.PsString[] = [{
p: rawBase.p.slice(0, -1) + "ږ",
f: rawBase.f.slice(0, -2) + "éG",
}];
const short: T.PsString[] | undefined = (verb.entry.shortIntrans)
? [{
p: rawBase.p.slice(0, -2),
f: rawBase.f.slice(0, -2),
}]
: undefined;
return short ? [{ long, short }] : [long]
}
return [ return [
[rawBase], [],
[
{
type: "VB",
ps: [makePsString(verb.entry.psp, verb.entry.psf)],
},
],
]; ];
} }
if (aspect === "perfective") { const rawBase = removeL(makePsString(verb.entry.p, verb.entry.f));
const base = (verb.entry.ssp && verb.entry.ssf) if (verb.entry.c?.includes("intrans.") && rawBase.p.endsWith("ېد")) {
// with irregular perfective stem const shortBase = trimOffPs(rawBase, 1, 2);
? makePsString(verb.entry.ssp, verb.entry.ssf) const long: T.PsString[] = [concatPsString(
: (verb.entry.psp && verb.entry.psf) shortBase,
{ p: "ږ", f: "éG" },
)];
const short: T.PsString[] | undefined = (verb.entry.shortIntrans)
? [shortBase]
: undefined;
const vb: T.VB = {
type: "VB",
ps: short ? { long, short } : long,
};
return [
[],
[vb],
];
}
return [
[],
[
{
type: "VB",
ps: [rawBase],
},
],
];
}
function getPerfectiveStem(verb: T.VerbEntryNoFVars, person: { gender: T.Gender, number: T.NounNumber }): T.RootsStemsOutput {
if (verb.entry.f === "tlul") {
return tlulPerfectiveStem(person);
}
const base = (verb.entry.ssp && verb.entry.ssf)
// with irregular perfective stem
? makePsString(verb.entry.ssp, verb.entry.ssf)
: (verb.entry.psp && verb.entry.psf)
// with perfective stem based on irregular perfective root // with perfective stem based on irregular perfective root
? makePsString(verb.entry.psp, verb.entry.psf) ? makePsString(verb.entry.psp, verb.entry.psf)
// with regular infinitive based perfective stem // with regular infinitive based perfective stem
: removeL(makePsString(verb.entry.p, verb.entry.f)); : removeL(makePsString(verb.entry.p, verb.entry.f));
const [perfectiveHead, rest] = getPerfectiveHead(base, verb); const [perfectiveHead, rest] = getPerfectiveHead(base, verb);
return [ return [
...perfectiveHead ? [perfectiveHead] : [], perfectiveHead ? [perfectiveHead] : [],
[rest], [
]; {
} type: "VB",
return []; ps: isKawulVerb(verb) ? {
long: [{ p: "کړ", f: "kR" }],
short: [{ p: "ک", f: "k" }],
} : [rest],
},
],
];
} }
function getPerfectiveHead(base: T.PsString, { entry }: T.VerbEntry): [T.PH, T.PsString] | [undefined, T.PsString] { // function getPastPart(verb: T.VerbEntryNoFVars, person: { gender: T.Gender, number: T.NounNumber }): T.SingleOrLengthOpts<T.PsString> {
// const root = getImperfectiveRoot(verb);
// // TODO: with new inflection engine more streamlined
// return inflectRoot
// }
function getPerfectiveHead(base: T.PsString, { entry }: T.VerbEntryNoFVars): [T.PH, T.PsString] | [undefined, T.PsString] {
// if ((verb.entry.ssp && verb.entry.ssf) || verb.entry.separationAtP) { // if ((verb.entry.ssp && verb.entry.ssf) || verb.entry.separationAtP) {
// // handle split // // handle split
// } // }
if (entry.separationAtP && entry.separationAtF) { if (entry.separationAtP && entry.separationAtF) {
const ph: T.PH = { const ph: T.PH = {
type: "PH", type: "PH",
ps: accentOnNFromEnd({ ps: {
p: base.p.slice(0, entry.separationAtP), p: base.p.slice(0, entry.separationAtP),
f: base.f.slice(0, entry.separationAtF), f: accentSyllable(base.f.slice(0, entry.separationAtF)),
}, 0), },
}; };
const rest = { const rest = {
p: base.p.slice(entry.separationAtP), p: base.p.slice(entry.separationAtP),
@ -195,8 +273,22 @@ function addUl(b: T.PsString): T.PsString {
// TODO: could do removeEndingL (slower but safer) // TODO: could do removeEndingL (slower but safer)
function removeL(ps: T.PsString): T.PsString { function removeL(ps: T.PsString): T.PsString {
return { return trimOffPs(ps, 1, 2);
p: ps.p.slice(0, -1),
f: ps.f.slice(0, -2),
};
} }
function tlulPerfectiveStem(person: { gender: T.Gender, number: T.NounNumber }): T.RootsStemsOutput {
return [
[
{
type: "PH",
ps: inflectPattern1({ p: "لاړ", f: "laaR" }, person).map(x => concatPsString(x, " "))[0],
},
],
[
{
type: "VB",
ps: [{ p: "ش", f: "sh" }],
},
],
];
}

View File

@ -12,22 +12,22 @@ export function vEntry(e: any, c?: any): T.VerbEntry {
export function getAllRs(verb: T.VerbEntry): { export function getAllRs(verb: T.VerbEntry): {
stem: { stem: {
perfective: T.RootStemOutput, perfective: T.RootsStemsOutput,
imperfective: T.RootStemOutput, imperfective: T.RootsStemsOutput,
}, },
root: { root: {
perfective: T.RootStemOutput, perfective: T.RootsStemsOutput,
imperfective: T.RootStemOutput, imperfective: T.RootsStemsOutput,
}, },
} { } {
return { return {
stem: { stem: {
perfective: getRootStem({ verb, type: "basic", part: { rs: "stem", aspect: "perfective" }, person: undefined }), perfective: getRootStem({ verb, type: "basic", part: { rs: "stem", aspect: "perfective" }, genderNumber: { gender: "masc", number: "singular" } }),
imperfective: getRootStem({ verb, type: "basic", part: { rs: "stem", aspect: "imperfective" }, person: undefined }), imperfective: getRootStem({ verb, type: "basic", part: { rs: "stem", aspect: "imperfective" }, genderNumber: { gender: "masc", number: "singular" } }),
}, },
root: { root: {
perfective: getRootStem({ verb, type: "basic", part: { rs: "root", aspect: "perfective" }, person: undefined }), perfective: getRootStem({ verb, type: "basic", part: { rs: "root", aspect: "perfective" }, genderNumber: { gender: "masc", number: "singular" } }),
imperfective: getRootStem({ verb, type: "basic", part: { rs: "root", aspect: "imperfective" }, person: undefined }), imperfective: getRootStem({ verb, type: "basic", part: { rs: "root", aspect: "imperfective" }, genderNumber: { gender: "masc", number: "singular" } }),
}, },
}; };
} }

View File

@ -24,6 +24,7 @@ import {
endsInShwa, endsInShwa,
splitPsByVarients, splitPsByVarients,
endsWith, endsWith,
trimOffPs,
} from "./p-text-helpers"; } from "./p-text-helpers";
import * as T from "../../types"; import * as T from "../../types";
import { import {
@ -704,6 +705,15 @@ test(`splitDoubleWord should work`, () => {
expect(splitDoubleWord(removeFVarients(orig))).toEqual(out); expect(splitDoubleWord(removeFVarients(orig))).toEqual(out);
}); });
test("trimOffPs should word", () => {
expect(trimOffPs({ p: "لیدل", f: "leedúl" }, 1, 2))
.toEqual({ p: "لید", f: "leed" });
expect(trimOffPs({ p: "کور", f: "kor" }, 2, 2))
.toEqual({ p: "ک", f: "k" });
expect(trimOffPs({ p: "کور", f: "kor" }, 0, 0))
.toEqual({ p: "کور", f: "kor" });
});
// test(`allThirdPersMascPlur should work`, () => { // test(`allThirdPersMascPlur should work`, () => {
// expect( // expect(
// allThirdPersMascPlur([ // allThirdPersMascPlur([

View File

@ -88,6 +88,22 @@ export function concatPsString(...items: Array<T.PsString | T.LengthOptions<T.Ps
}; };
} }
/**
* Trims off a given amount of characters of p and f in a PsString
* (also removes any other fields in the object)
*
* @param ps
* @param pOff - number of characters of pashto script to remove from the end
* @param fOff - number of characters of phonetics to remove from the end
* @returns
*/
export function trimOffPs(ps: T.PsString, pOff: number, fOff: number): T.PsString {
return {
p: pOff === 0 ? ps.p : ps.p.slice(0, -pOff),
f: fOff === 0 ? ps.f : ps.f.slice(0, -fOff),
};
}
/** /**
* breaks a dictionary entry with a double wording (ie. ګډ وډ) into two seperate words * breaks a dictionary entry with a double wording (ie. ګډ وډ) into two seperate words
* *

View File

@ -1074,45 +1074,47 @@ export type MiniPronoun = {
np: NPSelection, np: NPSelection,
}; };
export type VB = PH | VA | VI | PT | EQ | Welded; export type VerbRenderedOutput = [[VHead] | [], [VB, VBE] | [VBE]];
export type RootsStemsOutput = [[VHead] | [], [VB, VBA] | [VBA]]; // or perfect / equative
export type VB = VBBasic | VBGenNum | Welded;
export type VBA = Exclude<VB, VBGenNum>;
export type VBE = (VBBasic | Welded) & {
person: Person,
}; // or equative
export type VBBasic = {
type: "VB",
ps: SingleOrLengthOpts<PsString[]>,
};
export type VBGenNum = VBBasic & GenderNumber;
export type GenderNumber = {
gender: Gender,
number: NounNumber,
};
export type Welded = {
type: "welded",
left: NComp | VBBasic | Welded,
right: VBBasic,
};
export type VHead = PH | NComp;
/** perfective head block */ /** perfective head block */
export type PH = { export type PH = {
type: "PH", type: "PH",
ps: PsString, ps: PsString,
} | { };
type: "PHComp",
export type NComp = {
type: "NComp",
comp: Comp, comp: Comp,
}; };
/** verb block with person agreement */
export type VA = {
type: "VA",
ps: SingleOrLengthOpts<PsString[]>,
person: Person,
};
/** invariable verb block */
export type VI = {
type: "VI",
ps: SingleOrLengthOpts<PsString[]>,
};
/** participle block with inflection */
export type PT = {
type: "PT",
ps: SingleOrLengthOpts<PsString[]>,
gender: Gender,
number: NounNumber,
};
/** equative block */
export type EQ = {
type: "EQ",
ps: SingleOrLengthOpts<PsString[]>,
person: Person,
};
// Complement can be one of // Complement can be one of
// - adjective // - adjective
// - locative adv // - locative adv
@ -1131,13 +1133,3 @@ export type Comp = {
type: "NounComp", type: "NounComp",
ps: PsString, ps: PsString,
}; };
/** a veb block with a welded part having it's accents removed */
export type Welded = {
type: "welded",
/** accents must be removed from the left */
left: VI | Comp,
right: VA | PT | VI,
};
export type RootStemOutput = (PH | SingleOrLengthOpts<PsString[]>)[];