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,
voice: testVoice,
}) : 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">
<div className="d-block mx-auto card" style={{ maxWidth: "700px", background: "var(--closer)"}}>
<div className="card-body">
@ -252,9 +252,12 @@ function VPBuilderDemo({ opts }: {
role="subject"
opts={opts}
/>
<pre>
{JSON.stringify(rs, null, " ")}
</pre>
{/* <details>
<summary>Roots and Stems</summary>
<pre>
{JSON.stringify(rs, null, " ")}
</pre>
</details> */}
<pre>
{JSON.stringify(rv, null, " ")}
</pre>

View File

@ -104,7 +104,7 @@ const accentReplacer = [
{ vowel: "U", accented: "Ú" },
];
function accentSyllable(s: string): string {
export function accentSyllable(s: string): string {
return s.replace(/a|e|i|o|u|U/, (match) => {
const r = accentReplacer.find((x) => x.vowel === match);
/* 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[]> {
if ("long" in s) {
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 {
functionOnOptLengths,
getPersonInflectionsKey,
getVerbBlockPosFromPerson,
personGender,
personIsPlural,
personNumber,
} from "../misc-helpers";
import {
yulEndingInfinitive,
trimOffPs,
} from "../p-text-helpers";
import {
concatPsString,
@ -21,19 +18,14 @@ import {
} from "../grammar-units";
import { isKawulVerb, isAbilityTense, isPerfectTense, isTlulVerb } from "../type-predicates";
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 { makePsString, removeFVarients } from "../accent-and-ps-utils";
import { pashtoConsonants } from "../pashto-consonants";
import { accentOnNFromEnd, removeAccents } from "../accent-helpers";
import { getRootStem as newGetRootStem } from "./roots-and-stems";
import { accentPsSyllable } from "../accent-helpers";
import { getRootStem } from "./roots-and-stems";
const kedulStatVerb: T.VerbEntry = {
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,
};
// For the chart display of the results: base the length thing on the VBE at the end, if there are other
// 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
// works perfectly with stative compounds as well!
@ -56,302 +48,126 @@ export function renderVerb({ verb, tense, person, voice }: {
voice: T.Voice,
}): {
hasBa: boolean,
verbBlocks: T.VB[],
vbs: T.VerbRenderedOutput,
} {
// TODO: check for transitivity with passive voice ??
if (isPerfectTense(tense)) {
return getPerfectVBs({ verb, tense, person, voice });
}
const hasBa = tenseHasBa(tense);
if (isPerfectTense(tense)) {
throw new Error("not implemented yet");
}
const isPast = isPastTense(tense);
const aspect = getAspect(tense);
const isAbility = isAbilityTense(tense);
const noPerfective = isAbility && (voice === "passive" || isTlulVerb(verb) || isKedul(verb));
// 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 genderNumber = {
gender: personGender(person),
number: personNumber(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,
});
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,
});
// #2 add the verb ending to it
const ending = getEnding(person, isPast);
return {
hasBa,
verbBlocks: [
...(!noPerfective && perfectiveHead)
? [perfectiveHead] : [],
...verbBlocks,
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 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),
number: personNumber(person),
ps: chooseParticipleInflection(inflectYey(fromPersInfls(vInfo.participle.past, person)), person)
}
return {
hasBa,
verbBlocks: [participleBlock, equativeBlock],
};
}
function weld(left: T.VI, right: T.VA | T.PT | T.VI): T.Welded {
return {
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>,
function addEnding({ verb, rs, ending, person, pastThird, aspect }: {
rs: [T.VB, T.VBA] | [T.VBA],
ending: T.SingleOrLengthOpts<T.PsString[]>,
person: T.Person,
isPast: boolean,
verb: T.VerbEntry,
pastThird: boolean,
aspect: T.Aspect,
}): T.SingleOrLengthOpts<T.PsString[]> {
// TODO: no need for useless abbreviation now
const rs = rootStem;
const end = ending;
const idiosyncratic3rdPast = isPast && person === T.Person.ThirdSingMale;
const safeEndAdd = (rs: T.PsString) => (ending: T.PsString): T.PsString => (
(ending.p === "ل" && rs.p.slice(-1) === "ل")
? rs
: concatPsString(rs, ending)
);
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;
}): [T.VB, T.VBE] | [T.VBE] {
return rs.length === 2
? [rs[0], addEnd(rs[1], ending)]
: [addEnd(rs[0], ending)];
function addEnd(vba: T.VBA, ending: T.SingleOrLengthOpts<T.PsString[]>): T.VBE {
if (vba.type === "welded") {
return {
...vba,
right: addToVBBasicEnd(vba.right, ending),
person,
};
}
return {
long: endLong.map(safeEndAdd(rsLong)),
short: aspect === "imperfective"
? applyImperfectiveShortAccent(shortForm, yulEndingInfinitive(removeFVarients(verb.entry)))
: shortForm,
...addToVBBasicEnd(vba, ending),
person,
}
}
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");
}
return end.map((e) => concatPsString(rs, e));
}
function verbEndingConcat(ps: T.PsString[], end: T.PsString[]): T.PsString[] {
return ps.flatMap(v => (
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) {
@ -362,53 +178,12 @@ function getEnding(person: T.Person, isPast: boolean) {
} : presentEndings[row][col];
}
function chooseParticipleInflection(pinf: T.SingleOrLengthOpts<T.UnisexInflections>, p: T.Person): T.SingleOrLengthOpts<T.PsString[]> {
if ("long" in pinf) {
return {
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 === "شو") {
// TODO: THIS IS PROBABLY SKEEEETCH
function ensure3rdPast(ending: T.PsString[], rs: T.PsString[], verb: T.VerbEntry, aspect: T.Aspect): T.PsString[] {
if (isKedul(verb) && rs[0].p === "شو") {
return [{ p: "شو", f: "sho" }];
}
if (isKawulVerb(verb) && rs.p === "کړ") {
if (isKawulVerb(verb) && rs[0].p === "کړ") {
return [
{ p: "کړ", f: "kuR" },
{ p: "کړه", f: "kRu" },
@ -420,12 +195,12 @@ function ensure3rdPast(ending: T.PsString[], rs: T.PsString, verb: T.VerbEntry,
return [{ p: "غی", f: "ghey" }];
}
const specialTuShort: T.PsString = {
p: rs.p.slice(0, -1) + "ه",
f: rs.f.slice(0, -1) + (rs.f === "tl" ? "u" : "ú"),
p: rs[0].p.slice(0, -1) + "ه",
f: rs[0].f.slice(0, -1) + (rs[0].f === "tl" ? "u" : "ú"),
};
return [
specialTuShort,
concatPsString(rs, { p: "و", f: "o" }),
concatPsString(rs[0], { p: "و", f: "o" }),
];
}
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)
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" })];
}
const endsInDental = ["د", "ت"].includes(rs.p.slice(-1));
const endsInDental = ["د", "ت"].includes(rs[0].p.slice(-1));
// short endings like ورسېد
return (endsInDental ? [
"",
...ending,
] : ending).map(e => concatPsString(rs, e));
] : ending).map(e => concatPsString(rs[0], e));
}
function getAspect(tense: T.VerbTense | T.AbilityTense): T.Aspect {
@ -472,34 +247,6 @@ function isKedul(v: T.VerbEntry): boolean {
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 {
return t === "presentPerfect"
? "present"

View File

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

View File

@ -7,47 +7,83 @@
*/
import {
concatPsString,
concatPsString, trimOffPs,
} from "../p-text-helpers";
import * as T from "../../../types";
import { makePsString, removeFVarientsFromVerb } from "../accent-and-ps-utils";
import { accentOnNFromEnd, removeAccents } from "../accent-helpers";
import { assertNever } from "../assert-never";
import { accentOnNFromEnd, accentSyllable, removeAccents } from "../accent-helpers";
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,
part: {
rs: "root" | "stem",
aspect: T.Aspect,
} | {
participle: "present" | "past",
},
} | "pastPart",
type: "basic" | "ability" | "passive",
person: {
genderNumber: {
gender: T.Gender,
number: T.NounNumber,
} | undefined,
}): T.RootStemOutput {
},
}): T.RootsStemsOutput {
console.log({ type });
const v = removeFVarientsFromVerb(verb);
if ("participle" in part) {
return [];
if (part === "pastPart") {
throw new Error("not implemented yet");
}
console.log({ part });
return part.rs === "stem"
? getStem(v, part.aspect)
: getRoot(v, part.aspect);
? part.aspect === "imperfective"
? getImperfectiveStem(v)
: getPerfectiveStem(v, genderNumber)
: part.aspect === "imperfective"
? getImperfectiveRoot(v, type)
: getPerfectiveRoot(v);
}
function getRoot(verb: T.VerbEntryNoFVars, aspect: T.Aspect): T.RootStemOutput {
if (aspect === "imperfective") {
const infinitive = accentOnNFromEnd(makePsString(verb.entry.p, verb.entry.f), 0);
return [
function getImperfectiveRoot(verb: T.VerbEntryNoFVars, type: "basic" | "ability" | "passive"): T.RootsStemsOutput {
// if (type === "ability") {
// console.log("in ability");
// 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],
short: [addTrailingAccent(removeL(infinitive))],
type: "VB",
ps: {
long: [infinitive],
short: [addTrailingAccent(removeL(infinitive))],
},
},
];
}
// aspect === "perfective"
],
];
}
function getPerfectiveRoot(verb: T.VerbEntryNoFVars): T.RootsStemsOutput {
const base = removeAccents(
(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);
return [
...perfectiveHead ? [perfectiveHead] : [],
{
long: [rest],
short: [removeL(rest)],
},
perfectiveHead ? [perfectiveHead] : [],
[
{
type: "VB",
ps: {
long: [rest],
short: [removeL(rest)],
...isKawulVerb(verb) ? {
mini: [{ p: "ک", f: "k" }],
} : {},
},
},
],
];
}
function getStem(verb: T.VerbEntryNoFVars, aspect: T.Aspect): T.RootStemOutput {
if (aspect === "imperfective") {
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]
}
function getImperfectiveStem(verb: T.VerbEntryNoFVars): T.RootsStemsOutput {
if (verb.entry.psp && verb.entry.psf) {
return [
[rawBase],
[],
[
{
type: "VB",
ps: [makePsString(verb.entry.psp, verb.entry.psf)],
},
],
];
}
if (aspect === "perfective") {
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)
const rawBase = removeL(makePsString(verb.entry.p, verb.entry.f));
if (verb.entry.c?.includes("intrans.") && rawBase.p.endsWith("ېد")) {
const shortBase = trimOffPs(rawBase, 1, 2);
const long: T.PsString[] = [concatPsString(
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
? makePsString(verb.entry.psp, verb.entry.psf)
// with regular infinitive based perfective stem
: removeL(makePsString(verb.entry.p, verb.entry.f));
const [perfectiveHead, rest] = getPerfectiveHead(base, verb);
return [
...perfectiveHead ? [perfectiveHead] : [],
[rest],
];
}
return [];
const [perfectiveHead, rest] = getPerfectiveHead(base, verb);
return [
perfectiveHead ? [perfectiveHead] : [],
[
{
type: "VB",
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) {
// // handle split
// }
if (entry.separationAtP && entry.separationAtF) {
const ph: T.PH = {
type: "PH",
ps: accentOnNFromEnd({
ps: {
p: base.p.slice(0, entry.separationAtP),
f: base.f.slice(0, entry.separationAtF),
}, 0),
f: accentSyllable(base.f.slice(0, entry.separationAtF)),
},
};
const rest = {
p: base.p.slice(entry.separationAtP),
@ -195,8 +273,22 @@ function addUl(b: T.PsString): T.PsString {
// TODO: could do removeEndingL (slower but safer)
function removeL(ps: T.PsString): T.PsString {
return {
p: ps.p.slice(0, -1),
f: ps.f.slice(0, -2),
};
return trimOffPs(ps, 1, 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): {
stem: {
perfective: T.RootStemOutput,
imperfective: T.RootStemOutput,
perfective: T.RootsStemsOutput,
imperfective: T.RootsStemsOutput,
},
root: {
perfective: T.RootStemOutput,
imperfective: T.RootStemOutput,
perfective: T.RootsStemsOutput,
imperfective: T.RootsStemsOutput,
},
} {
return {
stem: {
perfective: getRootStem({ verb, type: "basic", part: { rs: "stem", aspect: "perfective" }, person: undefined }),
imperfective: getRootStem({ verb, type: "basic", part: { rs: "stem", aspect: "imperfective" }, 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" }, genderNumber: { gender: "masc", number: "singular" } }),
},
root: {
perfective: getRootStem({ verb, type: "basic", part: { rs: "root", aspect: "perfective" }, person: undefined }),
imperfective: getRootStem({ verb, type: "basic", part: { rs: "root", aspect: "imperfective" }, 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" }, genderNumber: { gender: "masc", number: "singular" } }),
},
};
}

View File

@ -24,6 +24,7 @@ import {
endsInShwa,
splitPsByVarients,
endsWith,
trimOffPs,
} from "./p-text-helpers";
import * as T from "../../types";
import {
@ -704,6 +705,15 @@ test(`splitDoubleWord should work`, () => {
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`, () => {
// expect(
// 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
*

View File

@ -1074,45 +1074,47 @@ export type MiniPronoun = {
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 */
export type PH = {
type: "PH",
ps: PsString,
} | {
type: "PHComp",
};
export type NComp = {
type: "NComp",
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
// - adjective
// - locative adv
@ -1131,13 +1133,3 @@ export type Comp = {
type: "NounComp",
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[]>)[];