@@ -252,9 +252,12 @@ function VPBuilderDemo({ opts }: {
role="subject"
opts={opts}
/>
-
- {JSON.stringify(rs, null, " ")}
-
+ {/*
+ Roots and Stems
+
+ {JSON.stringify(rs, null, " ")}
+
+ */}
{JSON.stringify(rv, null, " ")}
diff --git a/src/lib/src/accent-helpers.ts b/src/lib/src/accent-helpers.ts
index 066d149..347050a 100644
--- a/src/lib/src/accent-helpers.ts
+++ b/src/lib/src/accent-helpers.ts
@@ -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.SingleOrLengthOpts {
if ("long" in s) {
return {
diff --git a/src/lib/src/new-verb-engine/new-inflectors.ts b/src/lib/src/new-verb-engine/new-inflectors.ts
new file mode 100644
index 0000000..a375281
--- /dev/null
+++ b/src/lib/src/new-verb-engine/new-inflectors.ts
@@ -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" })];
+}
\ No newline at end of file
diff --git a/src/lib/src/new-verb-engine/render-verb.ts b/src/lib/src/new-verb-engine/render-verb.ts
index a1ee42d..3c62219 100644
--- a/src/lib/src/new-verb-engine/render-verb.ts
+++ b/src/lib/src/new-verb-engine/render-verb.ts
@@ -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 ??
+
+ const hasBa = tenseHasBa(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 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 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,
- 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,
- 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,
- 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",
+ const genderNumber = {
gender: personGender(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 {
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 {
- 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,
-} {
- 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 {
- // 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,
+function addEnding({ verb, rs, ending, person, pastThird, aspect }: {
+ rs: [T.VB, T.VBA] | [T.VBA],
ending: T.SingleOrLengthOpts,
person: T.Person,
- isPast: boolean,
verb: T.VerbEntry,
+ pastThird: boolean,
aspect: T.Aspect,
-}): T.SingleOrLengthOpts {
- // 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.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.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, p: T.Person): T.SingleOrLengthOpts {
- 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, aspect: T.Aspect): T.SingleOrLengthOpts {
- 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(x: T.OptionalPersonInflections, 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"
diff --git a/src/lib/src/new-verb-engine/roots-and-stems.test.ts b/src/lib/src/new-verb-engine/roots-and-stems.test.ts
index 4a63e01..e29fa77 100644
--- a/src/lib/src/new-verb-engine/roots-and-stems.test.ts
+++ b/src/lib/src/new-verb-engine/roots-and-stems.test.ts
@@ -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,
}[] =
[
{
@@ -615,7 +615,7 @@ const tests: {
}
]
}
- },
+ },
},
],
},
@@ -824,7 +824,7 @@ const tests: {
}
]
}
- },
+ },
},
{
verb: kedulDyn,
@@ -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);
diff --git a/src/lib/src/new-verb-engine/roots-and-stems.ts b/src/lib/src/new-verb-engine/roots-and-stems.ts
index 5c95792..8e08033 100644
--- a/src/lib/src/new-verb-engine/roots-and-stems.ts
+++ b/src/lib/src/new-verb-engine/roots-and-stems.ts
@@ -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 {
+// 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" }],
+ },
+ ],
+ ];
+}
\ No newline at end of file
diff --git a/src/lib/src/new-verb-engine/rs-helpers.ts b/src/lib/src/new-verb-engine/rs-helpers.ts
index 4d9a240..25b23e2 100644
--- a/src/lib/src/new-verb-engine/rs-helpers.ts
+++ b/src/lib/src/new-verb-engine/rs-helpers.ts
@@ -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" } }),
},
};
}
\ No newline at end of file
diff --git a/src/lib/src/p-text-helpers.test.ts b/src/lib/src/p-text-helpers.test.ts
index bdfd775..45cb9e4 100644
--- a/src/lib/src/p-text-helpers.test.ts
+++ b/src/lib/src/p-text-helpers.test.ts
@@ -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([
diff --git a/src/lib/src/p-text-helpers.ts b/src/lib/src/p-text-helpers.ts
index 0026213..1a6f7db 100644
--- a/src/lib/src/p-text-helpers.ts
+++ b/src/lib/src/p-text-helpers.ts
@@ -88,6 +88,22 @@ export function concatPsString(...items: Array;
+
+export type VBE = (VBBasic | Welded) & {
+ person: Person,
+}; // or equative
+
+export type VBBasic = {
+ type: "VB",
+ ps: SingleOrLengthOpts,
+};
+
+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,
- person: Person,
-};
-
-/** invariable verb block */
-export type VI = {
- type: "VI",
- ps: SingleOrLengthOpts,
-};
-
-/** participle block with inflection */
-export type PT = {
- type: "PT",
- ps: SingleOrLengthOpts,
- gender: Gender,
- number: NounNumber,
-};
-
-/** equative block */
-export type EQ = {
- type: "EQ",
- ps: SingleOrLengthOpts,
- 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)[];