From 305fb545b923579b7c0164d313cd9429950b435a Mon Sep 17 00:00:00 2001 From: adueck Date: Fri, 7 Apr 2023 15:48:33 +0400 Subject: [PATCH] more --- src/demo-components/VPBuilderDemo.tsx | 11 +- src/lib/src/accent-helpers.ts | 10 +- src/lib/src/new-verb-engine/new-inflectors.ts | 24 + src/lib/src/new-verb-engine/render-verb.ts | 479 +++++------------- .../new-verb-engine/roots-and-stems.test.ts | 58 ++- .../src/new-verb-engine/roots-and-stems.ts | 228 ++++++--- src/lib/src/new-verb-engine/rs-helpers.ts | 18 +- src/lib/src/p-text-helpers.test.ts | 10 + src/lib/src/p-text-helpers.ts | 16 + src/types.ts | 74 ++- 10 files changed, 420 insertions(+), 508 deletions(-) create mode 100644 src/lib/src/new-verb-engine/new-inflectors.ts diff --git a/src/demo-components/VPBuilderDemo.tsx b/src/demo-components/VPBuilderDemo.tsx index 058efb8..2f1e2b4 100644 --- a/src/demo-components/VPBuilderDemo.tsx +++ b/src/demo-components/VPBuilderDemo.tsx @@ -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
@@ -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)[];