From 3eb630a7b7f8a86f626947ca7416a3dc122a18cc Mon Sep 17 00:00:00 2001 From: adueck Date: Sat, 1 Apr 2023 22:19:16 +0400 Subject: [PATCH] more testing and refactoring --- src/lib/src/render-verb.test.ts | 2 +- src/lib/src/render-verb.ts | 243 ++++++++++++++++---------------- 2 files changed, 125 insertions(+), 120 deletions(-) diff --git a/src/lib/src/render-verb.test.ts b/src/lib/src/render-verb.test.ts index e458377..de9184e 100644 --- a/src/lib/src/render-verb.test.ts +++ b/src/lib/src/render-verb.test.ts @@ -1372,7 +1372,7 @@ test("ability simple verb forms", () => { ] }, }, - // exception with tlul verbs losing aspect + // exception with tlul verbs losing aspect { tense: "subjunctiveVerbModal", person: 10, diff --git a/src/lib/src/render-verb.ts b/src/lib/src/render-verb.ts index 6e4c408..dc5ac02 100644 --- a/src/lib/src/render-verb.ts +++ b/src/lib/src/render-verb.ts @@ -49,99 +49,91 @@ export function renderVerb({ verb, tense, person, voice }: { verbBlocks: T.VB[], } { // TODO: check for transitivity with passive voice ?? - - // WARNING: this only works with simple verbs - const hasBa = tenseHasBa(tense); if (isPerfectTense(tense)) { - return { - hasBa, - verbBlocks: getPerfectBlocks({ verb, tense, person, voice }), - }; + return getPerfectVBs({ verb, tense, person, voice }); } + const hasBa = tenseHasBa(tense); const isPast = isPastTense(tense); const aspect = getAspect(tense); const isAbility = isModalTense(tense); + const noPerfective = isAbility && (voice === "passive" || isTlulVerb(verb) || isKedul(verb)); const { perfectiveHead, rootStem } = getRootStem({ - verb, aspect, isPast, isAbility, person, voice + verb, aspect, isPast, isAbility, person, voice, noPerfective, }); - const perfectiveHeadBlock: T.PH | undefined = perfectiveHead ? { - type: "PH", - // should only need this for tlul and Daaredul? - ps: fromPersInfls(perfectiveHead, person), - } : undefined; + 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); - if (isAbility) { - const [vb, shPart] = getAbilityVerbBlocks({ verb, isPast, person, root: rootStem, aspect, voice }); - return { - hasBa, - verbBlocks: (perfectiveHeadBlock && voice === "active") - ? [perfectiveHeadBlock, vb, shPart] - : [vb, shPart], - }; - } - if (voice === "passive") { - const vbs = getPassiveVerbBlocks({ root: rootStem, tense, person }); - return { - hasBa, - verbBlocks: [ - ...perfectiveHeadBlock ? [perfectiveHeadBlock] : [], - vbs, - ], - }; - } - const verbBlock: T.VA = { + return [{ type: "VA", ps: addEnding({ rootStem, ending, person, isPast, verb, aspect, }), person, - }; - return { - hasBa, - verbBlocks: perfectiveHeadBlock ? [ - perfectiveHeadBlock, verbBlock, - ] : [verbBlock], - } + }]; } function getPassiveVerbBlocks({ root, tense, person }: { root: T.SingleOrLengthOpts, tense: T.VerbTense, person: T.Person, -}): T.Welded { - /* istanbul ignore next */ - if (!("long" in root)) { - throw new Error("should have length versions in roots for passive"); - } - const { verbBlocks: [auxVerb] } = renderVerb({ - verb: kedulStatVerb, - tense, - person, - voice: "active", - }) as { hasBa: boolean, verbBlocks: [T.VA] }; - return weld( - { - type: "VI", - ps: [root.long], - }, - auxVerb, - ); -} - -function getAbilityVerbBlocks({ verb, isPast, person, root, aspect, voice }: { - verb: T.VerbEntry, - isPast: boolean, - person: T.Person, - root: T.SingleOrLengthOpts, - aspect: T.Aspect, - voice: T.Voice, }): T.VB[] { /* istanbul ignore next */ if (!("long" in root)) { throw new Error("should have length versions in roots for passive"); } - const noPerfective = isTlulVerb(verb) || isKedul(verb); + 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( @@ -154,7 +146,7 @@ function getAbilityVerbBlocks({ verb, isPast, person, root, aspect, voice }: { ps: addAbilityTailsToRs({ long: { p: "کېدل", f: "kedúl" }, short: { p: "کېد", f: "ked" }, - }, aspect, true), + }, adjustedAspect), }, ), shBlock, @@ -163,38 +155,38 @@ function getAbilityVerbBlocks({ verb, isPast, person, root, aspect, voice }: { // TODO: this is redundant, we did it in another part of the program? const verbBlock: T.VI = { type: "VI", - ps: addAbilityTailsToRs(root, aspect, noPerfective), + 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 getPerfectBlocks({ verb, tense, person, voice }: { +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, -}): T.VB[] { +}): { hasBa: boolean, verbBlocks: T.VB[] } { + const hasBa = tenseHasBa(tense); const vInfo = getVerbInfo(verb.entry) as T.SimpleVerbInfo; - // TODO: put this in a seperate function? - if (voice === "passive") { const [pt, eq] = getKedulStatPerfect(person, tense); const passiveRoot: T.VI = { @@ -202,7 +194,10 @@ function getPerfectBlocks({ verb, tense, person, voice }: { ps: [noPersInfs(vInfo.root.imperfective).long], }; const welded: T.Welded = weld(passiveRoot, pt); - return [welded, eq]; + return { + hasBa, + verbBlocks: [welded, eq], + } } const equative = equativeEndings[perfectTenseToEquative(tense)]; @@ -222,7 +217,10 @@ function getPerfectBlocks({ verb, tense, person, voice }: { number: personNumber(person), ps: chooseParticipleInflection(inflectYey(noPersInfs(vInfo.participle.past)), person) } - return [participleBlock, equativeBlock]; + return { + hasBa, + verbBlocks: [participleBlock, equativeBlock], + }; } function weld(left: T.VI, right: T.VA | T.PT | T.VI): T.Welded { @@ -236,21 +234,21 @@ function weld(left: T.VI, right: T.VA | T.PT | T.VI): T.Welded { }; } -function getRootStem({ verb, aspect, isPast, isAbility, voice, person }: { +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.OptionalPersonInflections, + perfectiveHead: undefined | T.PH, rootStem: T.SingleOrLengthOpts, } { const vInfo = getVerbInfo(verb.entry) as T.SimpleVerbInfo; - const noPerfective = isTlulVerb(verb) || isKedul(verb); const rs = vInfo[(isPast || isAbility || voice === "passive") ? "root" : "stem"]; - if (noPerfective && isAbility) { + if (noPerfective) { // exception with tlul verbs for ability stems return { perfectiveHead: undefined, @@ -272,7 +270,10 @@ function getRootStem({ verb, aspect, isPast, isAbility, voice, person }: { const si = fromPersInfls(splitInfo, person); if ("long" in si) { return { - perfectiveHead: si.long[0], + perfectiveHead: { + type: "PH", + ps: fromPersInfls(si.long[0], person), + }, rootStem: { long: si.long[1], short: si.short[1], @@ -280,9 +281,13 @@ function getRootStem({ verb, aspect, isPast, isAbility, voice, person }: { } } else { return { - perfectiveHead: si[0], + perfectiveHead: { + type: "PH", + // should only need this for tlul and Daaredul? + ps: fromPersInfls(si[0], person), + }, rootStem: si[1], - } + }; } } } @@ -336,22 +341,6 @@ function getEnding(person: T.Person, isPast: boolean) { } : presentEndings[row][col]; } -function perfectTenseToEquative(t: T.PerfectTense): keyof typeof equativeEndings { - return t === "presentPerfect" - ? "present" - : t === "futurePerfect" - ? "habitual" - : t === "habitualPerfect" - ? "habitual" - : t === "pastPerfect" - ? "past" - : t === "pastSubjunctivePerfect" - ? "pastSubjunctive" - : t === "wouldBePerfect" - ? "past" - : "subjunctive" -} - function chooseParticipleInflection(pinf: T.SingleOrLengthOpts, p: T.Person): T.SingleOrLengthOpts { if ("long" in pinf) { return { @@ -364,7 +353,7 @@ function chooseParticipleInflection(pinf: T.SingleOrLengthOpts, aspect: T.Aspect, noPerfective: boolean): T.SingleOrLengthOpts { +function addAbilityTailsToRs(rs: T.LengthOptions, aspect: T.Aspect): T.SingleOrLengthOpts { const tails: T.PsString[] = [ { p: "ی", f: "ey" }, { p: "ای", f: "aay" }, @@ -374,12 +363,12 @@ function addAbilityTailsToRs(rs: T.LengthOptions, aspect: T.Aspect, { p: "ای", f: "áay" }, ]; // for single syllable long verb stems like tlul - ensure the accent - const psLong = (aspect === "perfective" && !noPerfective) + const psLong = (aspect === "perfective") ? removeAccents(rs.long) : ensureAccentLongStem(rs.long); return { long: tails.map(t => concatPsString(psLong, t)), - short: (aspect === "perfective" && !noPerfective ? tails : accentedTails) + short: (aspect === "perfective" ? tails : accentedTails) .map(t => concatPsString(rs.short, t)), }; } @@ -488,4 +477,20 @@ function getKedulStatPerfect(person: T.Person, tense: T.PerfectTense): [T.PT, T. 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" + : t === "futurePerfect" + ? "habitual" + : t === "habitualPerfect" + ? "habitual" + : t === "pastPerfect" + ? "past" + : t === "pastSubjunctivePerfect" + ? "pastSubjunctive" + : t === "wouldBePerfect" + ? "past" + : "subjunctive" } \ No newline at end of file