From 1658f021a03d48eee249bfe4e0938d82b6bbf6e4 Mon Sep 17 00:00:00 2001 From: adueck Date: Sun, 2 Apr 2023 18:53:12 +0400 Subject: [PATCH] working on new roots and stems getter to replace verbInfo --- .../src/vp-explorer/AllTensesDisplay.tsx | 4 +- .../src/vp-explorer/TensePicker.tsx | 12 +- .../src/vp-explorer/VPChartDisplay.tsx | 2 +- src/demo-components/VPBuilderDemo.tsx | 6 +- src/lib/src/accent-and-ps-utils.ts | 13 +- src/lib/src/human-readable.ts | 6 +- .../{ => new-verb-engine}/render-verb.test.ts | 8 +- .../src/{ => new-verb-engine}/render-verb.ts | 81 +++++--- .../new-verb-engine/roots-and-stems.test.ts | 181 ++++++++++++++++++ .../src/new-verb-engine/roots-and-stems.ts | 123 ++++++++++++ .../phrase-building/english-vp-rendering.ts | 4 +- src/lib/src/phrase-building/render-vp.ts | 8 +- src/lib/src/phrase-building/vp-tools.ts | 8 +- src/lib/src/type-predicates.ts | 2 +- src/types.ts | 35 +++- 15 files changed, 428 insertions(+), 65 deletions(-) rename src/lib/src/{ => new-verb-engine}/render-verb.test.ts (99%) rename src/lib/src/{ => new-verb-engine}/render-verb.ts (85%) create mode 100644 src/lib/src/new-verb-engine/roots-and-stems.test.ts create mode 100644 src/lib/src/new-verb-engine/roots-and-stems.ts diff --git a/src/components/src/vp-explorer/AllTensesDisplay.tsx b/src/components/src/vp-explorer/AllTensesDisplay.tsx index 06061c5..2a44780 100644 --- a/src/components/src/vp-explorer/AllTensesDisplay.tsx +++ b/src/components/src/vp-explorer/AllTensesDisplay.tsx @@ -28,8 +28,8 @@ function AllTensesDisplay({ VS, opts }: { VS: T.VerbSelection, opts: T.TextOptio : ("transitive" in rawConjugations) ? rawConjugations[VS.transitivity === "grammatically transitive" ? "grammaticallyTransitive" : "transitive"] : rawConjugations; - function getTense(baseTense: T.VerbTense | T.PerfectTense | T.ImperativeTense): T.VerbTense | T.PerfectTense | T.ImperativeTense | T.ModalTense { - return VS.tenseCategory === "modal" ? `${baseTense}Modal` as T.ModalTense : baseTense; + function getTense(baseTense: T.VerbTense | T.PerfectTense | T.ImperativeTense): T.VerbTense | T.PerfectTense | T.ImperativeTense | T.AbilityTense { + return VS.tenseCategory === "modal" ? `${baseTense}Modal` as T.AbilityTense : baseTense; } return
setShowFormulas(x => !x)}> diff --git a/src/components/src/vp-explorer/TensePicker.tsx b/src/components/src/vp-explorer/TensePicker.tsx index deeec23..91ad459 100644 --- a/src/components/src/vp-explorer/TensePicker.tsx +++ b/src/components/src/vp-explorer/TensePicker.tsx @@ -1,7 +1,7 @@ import Select from "react-select"; import * as T from "../../../types"; import ButtonSelect from "../ButtonSelect"; -import { isImperativeTense, isModalTense, isPerfectTense, isVerbTense } from "../../../lib/src/type-predicates"; +import { isImperativeTense, isAbilityTense, isPerfectTense, isVerbTense } from "../../../lib/src/type-predicates"; import useStickyState from "../useStickyState"; import { customStyles } from "../EntrySelect"; import { @@ -19,15 +19,15 @@ function composeFormula(formula: string, prefix: "passive" | "ability"): string .replace("past participle", `${prefix} past participle`); } -export function getRandomTense(o?: T.PerfectTense | T.VerbTense | T.ModalTense | T.ImperativeTense): T.PerfectTense | T.VerbTense | T.ModalTense | T.ImperativeTense { - let tns: T.PerfectTense | T.VerbTense | T.ModalTense | T.ImperativeTense; +export function getRandomTense(o?: T.PerfectTense | T.VerbTense | T.AbilityTense | T.ImperativeTense): T.PerfectTense | T.VerbTense | T.AbilityTense | T.ImperativeTense { + let tns: T.PerfectTense | T.VerbTense | T.AbilityTense | T.ImperativeTense; const oldTenseCategory = !o ? undefined : getTenseCategory(o); const tenseOptions = oldTenseCategory === "perfect" ? perfectTenseOptions : oldTenseCategory === "modal" - ? verbTenseOptions.map(x => ({ ...x, value: `${x.value}Modal` as T.ModalTense })) + ? verbTenseOptions.map(x => ({ ...x, value: `${x.value}Modal` as T.AbilityTense })) : oldTenseCategory === "imperative" ? imperativeTenseOptions : verbTenseOptions; @@ -238,14 +238,14 @@ function TensePicker(props: ({ export default TensePicker; -function getTenseCategory(tense: T.VerbTense | T.PerfectTense | T.ModalTense | T.ImperativeTense): "basic" | "perfect" | "modal" | "imperative" { +function getTenseCategory(tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense): "basic" | "perfect" | "modal" | "imperative" { if (isPerfectTense(tense)) { return "perfect"; } if (isVerbTense(tense)) { return "basic"; } - if (isModalTense(tense)) { + if (isAbilityTense(tense)) { return "modal"; } if (isImperativeTense(tense)) { diff --git a/src/components/src/vp-explorer/VPChartDisplay.tsx b/src/components/src/vp-explorer/VPChartDisplay.tsx index 97547e9..46b5c6d 100644 --- a/src/components/src/vp-explorer/VPChartDisplay.tsx +++ b/src/components/src/vp-explorer/VPChartDisplay.tsx @@ -6,7 +6,7 @@ import * as T from "../../../types"; function ChartDisplay({ conjugations, tense, opts, voice }: { conjugations: T.VerbConjugation, - tense: T.VerbTense | T.PerfectTense | T.ModalTense | T.ImperativeTense, + tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense, opts: T.TextOptions, voice: T.VerbSelection["voice"], }) { diff --git a/src/demo-components/VPBuilderDemo.tsx b/src/demo-components/VPBuilderDemo.tsx index c932a75..59b5d90 100644 --- a/src/demo-components/VPBuilderDemo.tsx +++ b/src/demo-components/VPBuilderDemo.tsx @@ -12,7 +12,7 @@ import { randomNumber, } from "../lib/src/misc-helpers"; import { entryFeeder } from "./entryFeeder"; -import { renderVerb } from "../lib/src/render-verb"; +import { renderVerb } from "../lib/src/new-verb-engine/render-verb"; import NPPronounPicker from "../components/src/np-picker/NPPronounPicker"; @@ -56,7 +56,7 @@ const testPerfectTenses: T.PerfectTense[] = [ "pastSubjunctivePerfect", ]; -const testAbilityTenses: T.ModalTense[] = testVerbTenses.map(t => `${t}Modal`); +const testAbilityTenses: T.AbilityTense[] = testVerbTenses.map(t => `${t}Modal`); const testTenses = [ ...testVerbTenses, @@ -76,7 +76,7 @@ function VPBuilderDemo({ opts }: { person: 0, }, "testPronoun"); const [testVoice, setTestVoice] = useStickyState("active", "testVoice"); - const [testTense, setTestTense] = useStickyState("presentVerb", "testTense"); + const [testTense, setTestTense] = useStickyState("presentVerb", "testTense"); // const onlyGrammTrans = (arr: Transitivity[]) => ( // arr.length === 1 && arr[0] === "grammatically transitive" // ); diff --git a/src/lib/src/accent-and-ps-utils.ts b/src/lib/src/accent-and-ps-utils.ts index ac4f2d2..d230d26 100644 --- a/src/lib/src/accent-and-ps-utils.ts +++ b/src/lib/src/accent-and-ps-utils.ts @@ -10,10 +10,21 @@ export function makePsString(p: string, f: string): T.PsString { return { p, f }; } +export function removeFVarientsFromVerb(v: T.VerbEntry): T.VerbEntryNoFVars { + const b = removeFVarients(v.entry); + return { + entry: b, + ...v.complement ? { + complement: removeFVarients(v.complement), + } : {}, + } as T.VerbEntryNoFVars; +} + +export function removeFVarients(x: T.VerbDictionaryEntry): T.VerbDictionaryEntryNoFVars; export function removeFVarients(x: T.DictionaryEntry): T.DictionaryEntryNoFVars; export function removeFVarients(x: T.PsString): T.PsStringNoFVars; export function removeFVarients(x: string): T.FStringNoFVars; -export function removeFVarients(x: string | T.PsString | T.DictionaryEntry): T.FStringNoFVars | T.PsStringNoFVars | T.DictionaryEntryNoFVars { +export function removeFVarients(x: string | T.PsString | T.DictionaryEntry | T.VerbDictionaryEntry): T.FStringNoFVars | T.PsStringNoFVars | T.DictionaryEntryNoFVars | T.VerbDictionaryEntryNoFVars { if (typeof x === "string") { return x.split(",")[0] as T.FStringNoFVars; } diff --git a/src/lib/src/human-readable.ts b/src/lib/src/human-readable.ts index 1e43368..1b3f722 100644 --- a/src/lib/src/human-readable.ts +++ b/src/lib/src/human-readable.ts @@ -1,6 +1,6 @@ import * as T from "../../types"; import { - isModalTense, + isAbilityTense, isPerfectTense, isImperativeTense, } from "./type-predicates"; @@ -24,7 +24,7 @@ function humanReadableVerbTense(tense: T.VerbTense): string { : "habitual continuous past"; } -function humanReadableModalTense(tense: T.ModalTense): string { +function humanReadableModalTense(tense: T.AbilityTense): string { const base = tense.replace("Modal", "") as T.VerbTense; return `${humanReadableVerbTense(base)} ability`; } @@ -55,7 +55,7 @@ function humanReadableImperativeTense(tense: T.ImperativeTense): string { } export function humanReadableVerbForm(f: T.VerbFormName): string { - return isModalTense(f) + return isAbilityTense(f) ? humanReadableModalTense(f) : isPerfectTense(f) ? humanReadablePerfectTense(f) diff --git a/src/lib/src/render-verb.test.ts b/src/lib/src/new-verb-engine/render-verb.test.ts similarity index 99% rename from src/lib/src/render-verb.test.ts rename to src/lib/src/new-verb-engine/render-verb.test.ts index de9184e..db68294 100644 --- a/src/lib/src/render-verb.test.ts +++ b/src/lib/src/new-verb-engine/render-verb.test.ts @@ -1,7 +1,7 @@ -import * as T from "../../types"; +import * as T from "../../../types"; import { renderVerb } from "./render-verb"; -function vEntry(e: any, c?: any): T.VerbEntry { +export function vEntry(e: any, c?: any): T.VerbEntry { return { entry: e, ...c ? { @@ -1214,7 +1214,7 @@ test("perfect simple verb forms", () => { }); test("ability simple verb forms", () => { - const tests: { verb: T.VerbEntry, tense: T.ModalTense, person: T.Person, output: ReturnType }[] = [ + const tests: { verb: T.VerbEntry, tense: T.AbilityTense, person: T.Person, output: ReturnType }[] = [ { tense: "presentVerbModal", verb: ganul, @@ -1625,7 +1625,7 @@ test("passive perfect simple verbs", () => { }); test("passive ability simple verbs", () => { - const tests: { verb: T.VerbEntry, tense: T.ModalTense, person: T.Person, output: ReturnType }[] = [ + const tests: { verb: T.VerbEntry, tense: T.AbilityTense, person: T.Person, output: ReturnType }[] = [ { verb: ganul, tense: "presentVerbModal", diff --git a/src/lib/src/render-verb.ts b/src/lib/src/new-verb-engine/render-verb.ts similarity index 85% rename from src/lib/src/render-verb.ts rename to src/lib/src/new-verb-engine/render-verb.ts index dc5ac02..a1ee42d 100644 --- a/src/lib/src/render-verb.ts +++ b/src/lib/src/new-verb-engine/render-verb.ts @@ -1,47 +1,57 @@ -import * as T from "../../types"; +import * as T from "../../../types"; import { functionOnOptLengths, getPersonInflectionsKey, getVerbBlockPosFromPerson, - noPersInfs, personGender, personIsPlural, personNumber, -} from "./misc-helpers"; +} from "../misc-helpers"; import { yulEndingInfinitive, -} from "./p-text-helpers"; +} from "../p-text-helpers"; import { concatPsString, getLength, -} from "./p-text-helpers"; +} from "../p-text-helpers"; import { presentEndings, pastEndings, equativeEndings, -} from "./grammar-units"; -import { isKawulVerb, isModalTense, isPerfectTense, isTlulVerb } from "./type-predicates"; -import { tenseHasBa } from "./phrase-building/vp-tools"; -import { inflectYey } from "./pashto-inflector"; +} 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"; +} 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"; 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, }; +// TODO: Amazingly, the basic formula with the roots and stems from the basic verbs +// works perfectly with stative compounds as well! +// The only issue is that if we want to include more information (complement noun gender etc) in the blocks +// we need to redo the stem building to have those parts +// 2 options: +// 1. redo the root/stem builder to output primitive blocks +// 2. rebuild the roots/stems in the verb engine +// Will start with number 2 and then if I go back and rebuild the root/stem builder +// We can go back to using a very simple verb building formula + // TODO: problem with laaR - no perfective split // TODO: are azmóyulum and wáayulo really not just azmoyúlum and waayúlo ? // TODO: automatic 3rd person idiosyncronizing of raTul raaTu, shaRul, shaaRu, rasedul rased etc export function renderVerb({ verb, tense, person, voice }: { verb: T.VerbEntry, - tense: T.VerbTense | T.PerfectTense | T.ModalTense, // TODO: make T.Tense + tense: T.VerbTense | T.PerfectTense | T.AbilityTense, // TODO: make T.Tense person: T.Person, voice: T.Voice, }): { @@ -52,26 +62,37 @@ export function renderVerb({ verb, tense, person, voice }: { if (isPerfectTense(tense)) { return getPerfectVBs({ verb, tense, person, voice }); } + const hasBa = tenseHasBa(tense); const isPast = isPastTense(tense); const aspect = getAspect(tense); - const isAbility = isModalTense(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, - }); + ? getPassiveVerbBlocks({ root: rootStem, tense, person }) + : getBasicVerbBlock({ + rootStem, person, isPast, verb, aspect, + }); return { hasBa, verbBlocks: [ - ...!noPerfective && perfectiveHead ? [perfectiveHead] : [], - ...verbBlocks, + ...(!noPerfective && perfectiveHead) + ? [perfectiveHead] : [], + ...verbBlocks, ], }; } @@ -185,19 +206,19 @@ function getPerfectVBs({ verb, tense, person, voice }: { voice: T.Voice, }): { hasBa: boolean, verbBlocks: T.VB[] } { const hasBa = tenseHasBa(tense); - const vInfo = getVerbInfo(verb.entry) as T.SimpleVerbInfo; + 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: [noPersInfs(vInfo.root.imperfective).long], + ps: [fromPersInfls(vInfo.root.imperfective, person).long], }; const welded: T.Welded = weld(passiveRoot, pt); return { hasBa, verbBlocks: [welded, eq], - } + }; } const equative = equativeEndings[perfectTenseToEquative(tense)]; @@ -215,7 +236,7 @@ function getPerfectVBs({ verb, tense, person, voice }: { type: "PT", gender: personGender(person), number: personNumber(person), - ps: chooseParticipleInflection(inflectYey(noPersInfs(vInfo.participle.past)), person) + ps: chooseParticipleInflection(inflectYey(fromPersInfls(vInfo.participle.past, person)), person) } return { hasBa, @@ -246,13 +267,13 @@ function getRootStem({ verb, aspect, isPast, isAbility, voice, person, noPerfect perfectiveHead: undefined | T.PH, rootStem: T.SingleOrLengthOpts, } { - const vInfo = getVerbInfo(verb.entry) as T.SimpleVerbInfo; + 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: noPersInfs(rs.imperfective), + rootStem: fromPersInfls(rs.imperfective, person), }; } if (aspect === "perfective" && rs.perfectiveSplit) { @@ -262,7 +283,7 @@ function getRootStem({ verb, aspect, isPast, isAbility, voice, person, noPerfect perfectiveHead: undefined, // because the persInfs only happen with stative compound verbs,j // which we are handling differently now - rootStem: noPersInfs(rs[aspect]), + rootStem: fromPersInfls(rs[aspect], person), } function extractPerfectiveSplit(splitInfo: T.SplitInfo): ReturnType { @@ -438,7 +459,7 @@ function ensure3rdPast(ending: T.PsString[], rs: T.PsString, verb: T.VerbEntry, ] : ending).map(e => concatPsString(rs, e)); } -function getAspect(tense: T.VerbTense | T.ModalTense): T.Aspect { +function getAspect(tense: T.VerbTense | T.AbilityTense): T.Aspect { const t = tense.replace("Modal", ""); if (["presentVerb", "imperfectiveFuture", "imperfectivePast", "habitualImperfectivePast"].includes(t)) { return "imperfective"; 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 new file mode 100644 index 0000000..8432be9 --- /dev/null +++ b/src/lib/src/new-verb-engine/roots-and-stems.test.ts @@ -0,0 +1,181 @@ +import * as T from "../../../types"; +import { getRootStem } from "./roots-and-stems"; +import { vEntry } from "./render-verb.test"; +import { ooPrefix } from "./roots-and-stems"; + +const wahul = vEntry({"ts":1527815399,"i":15049,"p":"وهل","f":"wahul","g":"wahul","e":"to hit","r":4,"c":"v. trans.","tppp":"واهه","tppf":"waahu","ec":"hit,hits,hitting,hit,hit"}); +const achawul = vEntry({"ts":1527811872,"i":224,"p":"اچول","f":"achawul","g":"achawul","e":"to put, pour, drop, throw, put on","r":4,"c":"v. trans.","ec":"put,puts,putting,put,put"}); +const ganul = vEntry({"ts":1527812000,"i":11398,"p":"ګڼل","f":"gaNul, guNul","g":"gaNul,guNul","e":"to count, consider, reckon, suppose, assume","r":4,"c":"v. trans.","tppp":"ګاڼه","tppf":"gaaNu","ec":"deem"}); +const kawulStat = vEntry({"ts":1579015359582,"i":11030,"p":"کول","f":"kawul","g":"kawul","e":"to make ____ ____ (as in \"He's making me angry.\")","r":4,"c":"v. trans.","ssp":"کړ","ssf":"kR","prp":"کړل","prf":"kRul","pprtp":"کړی","pprtf":"kúRey","noOo":true,"ec":"make,makes,making,made,made"}); +const kawulDyn = vEntry({"ts":1527812752,"i":11031,"p":"کول","f":"kawul","g":"kawul","e":"to do (an action or activity)","r":4,"c":"v. trans./gramm. trans.","ssp":"وکړ","ssf":"óokR","prp":"وکړل","prf":"óokRul","pprtp":"کړی","pprtf":"kúRey","diacExcept":true,"ec":"do,does,doing,did,done"}); +const kedulStat = vEntry({"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"}); +const kedulDyn = vEntry({"ts":1527812754,"i":11101,"p":"کېدل","f":"kedul","g":"kedul","e":"to happen, occur","r":2,"c":"v. intrans.","ssp":"وش","ssf":"óosh","prp":"وشول","prf":"óoshwul","pprtp":"شوی","pprtf":"shúwey","diacExcept":true,"ec":"happen"}); +const raatlul = vEntry({"ts":1527815216,"i":6875,"p":"راتلل","f":"raatlúl","g":"raatlul","e":"to come","r":4,"c":"v. intrans.","psp":"راځ","psf":"raadz","ssp":"راش","ssf":"ráash","prp":"راغلل","prf":"ráaghlul","pprtp":"راغلی","pprtf":"raaghúley","tppp":"راغی","tppf":"ráaghey","noOo":true,"separationAtP":2,"separationAtF":3,"ec":"come,comes,coming,came,come"}); +const wartlul = vEntry({"ts":1585228579997,"i":14821,"p":"ورتلل","f":"wărtlul","g":"wartlul","e":"to come / go over to (third person or place)","r":4,"c":"v. intrans.","psp":"ورځ","psf":"wărdz","ssp":"ورش","ssf":"wársh","prp":"ورغلل","prf":"wárghlul","pprtp":"ورغلی","pprtf":"wărghúley","tppp":"ورغی","tppf":"wărghey","noOo":true,"separationAtP":2,"separationAtF":3,"ec":"come,comes,coming,came,come"}); +const tlul = vEntry({"ts":1527815348,"i":3791,"p":"تلل","f":"tlul","g":"tlul","e":"to go","r":4,"c":"v. intrans.","psp":"ځ","psf":"dz","ssp":"لاړ ش","ssf":"láaR sh","prp":"لاړ","prf":"láaR","ec":"go,goes,going,went,gone"}); +const awuxtul = vEntry({"ts":1527814012,"i":1133,"p":"اوښتل","f":"awUxtul","g":"awUxtul","e":"to pass over, overturn, be flipped over, spill over, shift, change, diverge, pass, cross, abandon; to be sprained","r":4,"c":"v. intrans.","psp":"اوړ","psf":"awR","ec":"pass","ep":"over"}); +const khorul = vEntry({"ts":1527812790,"i":6002,"p":"خوړل","f":"khoRul","g":"khoRul","e":"to eat, to bite","r":4,"c":"v. trans.","psp":"خور","psf":"khor","tppp":"خوړ","tppf":"khoR","ec":"eat,eats,eating,ate,eaten"}); +const azmoyul = vEntry({"ts":1527811605,"i":468,"p":"ازمویل","f":"azmoyul","g":"azmoyul","e":"to attempt, try; to experiment, test","r":4,"c":"v. trans.","sepOo":true,"ec":"try"}); +const khatul = vEntry({"ts":1527814025,"i":5677,"p":"ختل","f":"khatul","g":"khatul","e":"to climb, ascend, rise, go up; to fall out, to fall off, to leave/dissapear; to turn out to be ...; to give a sentence (in law)","r":3,"c":"v. intrans.","psp":"خېژ","psf":"khejz","tppp":"خوت","tppf":"khot","ec":"climb"}); +const rasedul = vEntry({"ts":1527813573,"i":7057,"p":"رسېدل","f":"rasedul","g":"rasedul","e":"arrive, reach; (fig.) understand, attain to; mature, ripen","r":4,"c":"v. intrans.","shortIntrans":true,"ec":"arrive"}); +const weshul = vEntry({"ts":1527811701,"i":15106,"p":"وېشل","f":"weshul","g":"weshul","e":"divide, distribute, share","r":4,"c":"v. trans.","ec":"divide"}); + +type RSO = ReturnType; +function getAllRs(verb: T.VerbEntry): (typeof regularVerbs)[0]["result"] { + 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 }), + }, + 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 }), + }, + }; +} + +const regularVerbs: { + verb: T.VerbEntry, + result: Record<"stem" | "root", { + imperfective: RSO, + perfective: RSO, + }> +}[] = [ + { + verb: weshul, + result: { + stem: { + perfective: [ + ooPrefix, + [{ p: "وېش", f: "wesh" }], + ], + imperfective: [ + [{ p: "وېش", f: "wesh" }], + ], + }, + root: { + perfective: [ + ooPrefix, + { + long: [{ p: "وېشل", f: "weshul" }], + short: [{ p: "وېش", f: "wesh" }], + }, + ], + imperfective: [ + { + long: [{ p: "وېشل", f: "weshúl" }], + short: [{ p: "وېش", f: "weshX" }], + }, + ], + }, + }, + }, + { + verb: ganul, + result: { + stem: { + perfective: [ + ooPrefix, + [{ p: "ګڼ", f: "gaN" }], + ], + imperfective: [ + [{ p: "ګڼ", f: "gaN" }], + ], + }, + root: { + perfective: [ + ooPrefix, + { + long: [{ p: "ګڼل", f: "gaNul" }], + short: [{ p: "ګڼ", f: "gaN" }], + }, + ], + imperfective: [ + { + long: [{ p: "ګڼل", f: "gaNúl" }], + short: [{ p: "ګڼ", f: "gaNX" }], + }, + ], + }, + }, + }, +]; + +const verbsWithIrregularStems: { + verb: T.VerbEntry, + result: Record<"stem" | "root", { + imperfective: RSO, + perfective: RSO, + }> +}[] = [ + { + verb: khorul, + result: { + stem: { + perfective: [ + ooPrefix, + [{ p: "خور", f: "khor" }], + ], + imperfective: [ + [{ p: "خور", f: "khor" }], + ], + }, + root: { + perfective: [ + ooPrefix, + { + long: [{ p: "خوړل", f: "khoRul" }], + short: [{ p: "خوړ", f: "khoR" }], + }, + ], + imperfective: [ + { + long: [{ p: "خوړل", f: "khoRúl" }], + short: [{ p: "خوړ", f: "khoRX" }], + }, + ], + }, + }, + }, + { + verb: khatul, + result: { + stem: { + perfective: [ + ooPrefix, + [{ p: "خېژ", f: "khejz" }], + ], + imperfective: [ + [{ p: "خېژ", f: "khejz" }], + ], + }, + root: { + perfective: [ + ooPrefix, + { + long: [{ p: "ختل", f: "khatul" }], + short: [{ p: "خت", f: "khat" }], + }, + ], + imperfective: [ + { + long: [{ p: "ختل", f: "khatúl" }], + short: [{ p: "خت", f: "khatX" }], + }, + ], + }, + }, + }, +]; + +test("basic roots and stems with regular verbs", () => { + regularVerbs.forEach(({ verb, result }) => { + expect(getAllRs(verb)).toEqual(result); + }); +}); + +test("baisc roots and stems with verbs with irregular stems", () => { + verbsWithIrregularStems.forEach(({ verb, result }) => { + expect(getAllRs(verb)).toEqual(result); + }); +}); \ No newline at end of file diff --git a/src/lib/src/new-verb-engine/roots-and-stems.ts b/src/lib/src/new-verb-engine/roots-and-stems.ts new file mode 100644 index 0000000..7d8692b --- /dev/null +++ b/src/lib/src/new-verb-engine/roots-and-stems.ts @@ -0,0 +1,123 @@ +/** + * Copyright (c) 2023 lingdocs.com + * + * This source code is licensed under the GPL3 license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import { + concatPsString, +} from "../p-text-helpers"; +import * as T from "../../../types"; +import { makePsString, removeFVarientsFromVerb } from "../accent-and-ps-utils"; +import { accentOnNFromEnd, removeAccents } from "../accent-helpers"; + +type RootStemOutput = (T.PH | T.SingleOrLengthOpts)[]; + +export const ooPrefix: T.PH = { + type: "PH", + ps: { p: "و", f: "óo" }, +}; + +export function getRootStem({ verb, part, type, person }: { + verb: T.VerbEntry, + part: { + rs: "root" | "stem", + aspect: T.Aspect, + } | { + participle: "present" | "past", + }, + type: "basic" | "ability" | "passive", + person: { + gender: T.Gender, + number: T.NounNumber, + } | undefined, +}): RootStemOutput { + const v = removeFVarientsFromVerb(verb); + if ("participle" in part) { + return []; + } + if (part.rs === "stem") { + return getStem(v, part.aspect); + } else { + return getRoot(v, part.aspect); + } +} + +function getRoot(verb: T.VerbEntryNoFVars, aspect: T.Aspect): RootStemOutput { + if (aspect === "imperfective") { + const infinitive = accentOnNFromEnd(makePsString(verb.entry.p, verb.entry.f), 0); + return [ + { + long: [infinitive], + short: [addTrailingAccent(removeL(infinitive))], + }, + ]; + } + if (aspect === "perfective") { + const base = removeAccents( + (verb.entry.prp && verb.entry.prf) + ? makePsString(verb.entry.prp, verb.entry.prf) + : makePsString(verb.entry.p, verb.entry.f) + ); + // TODO: determine ph and base + return [ + ooPrefix, + { + long: [base], + short: [removeL(base)], + }, + ]; + } + throw new Error("unknown aspect"); +} + +function getStem(verb: T.VerbEntryNoFVars, aspect: T.Aspect): RootStemOutput { + if (aspect === "imperfective") { + const base = (verb.entry.psp && verb.entry.psf) + // with irregular imperfective stem + ? makePsString(verb.entry.psp, verb.entry.psf) + // with regular infinitive based imperfective stem + : removeL(makePsString(verb.entry.p, verb.entry.f)); + return [ + [base], + ]; + } + 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) + // 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)); + // TODO: determine ph and base + return [ + ooPrefix, + [base], + ]; + } + return []; +} + +function addTrailingAccent(ps: T.PsString): T.PsString { + return { + p: ps.p, + f: ps.f + "X", + }; +} + +function addUl(b: T.PsString): T.PsString { + return concatPsString(b, { p: "ل", f: "ul" }); +} + +// 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), + }; +} diff --git a/src/lib/src/phrase-building/english-vp-rendering.ts b/src/lib/src/phrase-building/english-vp-rendering.ts index f9664ec..068e879 100644 --- a/src/lib/src/phrase-building/english-vp-rendering.ts +++ b/src/lib/src/phrase-building/english-vp-rendering.ts @@ -88,7 +88,7 @@ export function renderEnglishVPBase({ subjectPerson, object, vs }: { ]), }; const modalBuilders: Record< - T.ModalTense, + T.AbilityTense, (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => string[] > = { presentVerbModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ @@ -215,7 +215,7 @@ export function renderEnglishVPBase({ subjectPerson, object, vs }: { ]), } const passiveModalBuilders: Record< - T.ModalTense, + T.AbilityTense, (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => string[] > = { presentVerbModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ diff --git a/src/lib/src/phrase-building/render-vp.ts b/src/lib/src/phrase-building/render-vp.ts index a1b11f6..e7833e8 100644 --- a/src/lib/src/phrase-building/render-vp.ts +++ b/src/lib/src/phrase-building/render-vp.ts @@ -21,7 +21,7 @@ import { isAdjectiveEntry, isImperativeTense, isLocativeAdverbEntry, - isModalTense, + isAbilityTense, isNounEntry, isPattern4Entry, isPerfectTense, @@ -505,7 +505,7 @@ export function isStativeHelper(v: T.VerbEntry): boolean { } function splitUpIfModal(v: T.VerbRenderedBlock): [T.VerbRenderedBlock] | [T.ModalVerbBlock, T.ModalVerbKedulPart] { - if (!isModalTense(v.block.tense)) { + if (!isAbilityTense(v.block.tense)) { return [v]; } const [vrb, k] = splitOffLeapfrogWordFull(v.block.ps); @@ -561,8 +561,8 @@ function getPsVerbConjugation(conj: T.VerbConjugation, vs: T.VerbSelectionComple const hasBa = hasBaParticle(getLong(verbForm)[0]); if (perfective) { const past = isPastTense(vs.tense); - const splitInfo = conj.info[(past || isModalTense(vs.tense)) ? "root" : "stem"].perfectiveSplit; - if (!splitInfo || (isTlulVerb(vs.verb.entry) && isModalTense(vs.tense))) { + const splitInfo = conj.info[(past || isAbilityTense(vs.tense)) ? "root" : "stem"].perfectiveSplit; + if (!splitInfo || (isTlulVerb(vs.verb.entry) && isAbilityTense(vs.tense))) { return { ps: { head: undefined, rest: removeBaFromForm(verbForm) }, hasBa }; } // TODO: Either solve this in the inflector or here, it seems silly (or redundant) diff --git a/src/lib/src/phrase-building/vp-tools.ts b/src/lib/src/phrase-building/vp-tools.ts index 13ee43a..799ead4 100644 --- a/src/lib/src/phrase-building/vp-tools.ts +++ b/src/lib/src/phrase-building/vp-tools.ts @@ -38,7 +38,7 @@ export function isInvalidSubjObjCombo(subj: T.Person, obj: T.Person): boolean { */ export function getTenseVerbForm( conjR: T.VerbConjugation, - tense: T.VerbTense | T.PerfectTense | T.ModalTense | T.ImperativeTense, + tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense, voice: "active" | "passive", mode: "charts" | "phrase-building", negative: boolean, @@ -152,8 +152,8 @@ export function removeBa(ps: T.PsString): T.PsString { return psRemove(ps, concatPsString(grammarUnits.baParticle, " ")); } -export function getTenseFromVerbSelection(vs: T.VerbSelection): T.VerbTense | T.PerfectTense | T.ModalTense | T.ImperativeTense { - function verbTenseToModalTense(tn: T.VerbTense): T.ModalTense { +export function getTenseFromVerbSelection(vs: T.VerbSelection): T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense { + function verbTenseToModalTense(tn: T.VerbTense): T.AbilityTense { if (tn === "presentVerb") { return "presentVerbModal"; } @@ -198,7 +198,7 @@ export function isPastTense(tense: T.Tense): boolean { return tense.toLowerCase().includes("past"); } -export function tenseHasBa(tense: T.VerbTense | T.PerfectTense | T.ModalTense | T.ImperativeTense): boolean { +export function tenseHasBa(tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense): boolean { return [ "imperfectiveFuture", "perfectiveFuture", diff --git a/src/lib/src/type-predicates.ts b/src/lib/src/type-predicates.ts index 4527993..d54d0e2 100644 --- a/src/lib/src/type-predicates.ts +++ b/src/lib/src/type-predicates.ts @@ -222,7 +222,7 @@ export function isVerbTense(tense: T.Tense): tense is T.VerbTense { return verbTenses.some(x => x === tense); } -export function isModalTense(tense: T.Tense): tense is T.ModalTense { +export function isAbilityTense(tense: T.Tense): tense is T.AbilityTense { return tense.endsWith("Modal"); } diff --git a/src/types.ts b/src/types.ts index 8d8e17e..c842595 100644 --- a/src/types.ts +++ b/src/types.ts @@ -142,6 +142,11 @@ export type DictionaryEntry = { } export type DictionaryEntryNoFVars = DictionaryEntry & { __brand: "name for a dictionary entry with all the phonetics variations removed" }; +export type VerbDictionaryEntryNoFVars = VerbDictionaryEntry & { __brand2: "name for a verb dictionary entry with all the phonetics variations removed" }; +export type VerbEntryNoFVars = { + entry: VerbDictionaryEntryNoFVars, + complement?: DictionaryEntryNoFVars, +} & { __brand: "name for a verb entry with all the phonetics variations removed" }; export type PsStringNoFVars = PsString & { __brand: "name for a ps string with all the phonetics variations removed" }; export type FStringNoFVars = string & { __brand: "name for a phonetics string with all the phonetics variations removed" }; @@ -612,9 +617,9 @@ export type NounNumber = "singular" | "plural"; export type EquativeTense = "present" | "subjunctive" | "habitual" | "past" | "future" | "wouldBe" | "pastSubjunctive" | "wouldHaveBeen"; export type PerfectTense = `${EquativeTense}Perfect`; -export type ModalTense = `${VerbTense}Modal`; +export type AbilityTense = `${VerbTense}Modal`; export type ImperativeTense = `${Aspect}Imperative`; -export type Tense = EquativeTense | VerbTense | PerfectTense | ModalTense | ImperativeTense; +export type Tense = EquativeTense | VerbTense | PerfectTense | AbilityTense | ImperativeTense; export type SubjectSelection = { type: "subjectSelection", @@ -655,7 +660,7 @@ export type VPSelectionComplete = { form: FormVersion, }; -export type VerbFormName = VerbTense | PerfectTense | ModalTense | ImperativeTense; +export type VerbFormName = VerbTense | PerfectTense | AbilityTense | ImperativeTense; export type VerbSelectionComplete = Omit & { tense: VerbFormName, @@ -1075,6 +1080,9 @@ export type VB = PH | VA | VI | PT | EQ | Welded; export type PH = { type: "PH", ps: PsString, +} | { + type: "PHComp", + comp: Comp, }; /** verb block with person agreement */ @@ -1105,11 +1113,30 @@ export type EQ = { person: Person, }; +// Complement can be one of +// - adjective +// - locative adv +// - sandwich (TODO) +// - noun +/** complement block */ +export type Comp = { + type: "AdjComp", + ps: PsString, + gender: Gender, + number: NounNumber, +} | { + type: "LocAdvComp", + ps: PsString, +} | { + 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, // TODO - will get more complex with compounds + left: VI | Comp, right: VA | PT | VI, };