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,
};