working on new roots and stems getter to replace verbInfo

This commit is contained in:
adueck 2023-04-02 18:53:12 +04:00
parent 3eb630a7b7
commit 1658f021a0
15 changed files with 428 additions and 65 deletions

View File

@ -28,8 +28,8 @@ function AllTensesDisplay({ VS, opts }: { VS: T.VerbSelection, opts: T.TextOptio
: ("transitive" in rawConjugations) : ("transitive" in rawConjugations)
? rawConjugations[VS.transitivity === "grammatically transitive" ? "grammaticallyTransitive" : "transitive"] ? rawConjugations[VS.transitivity === "grammatically transitive" ? "grammaticallyTransitive" : "transitive"]
: rawConjugations; : rawConjugations;
function getTense(baseTense: T.VerbTense | T.PerfectTense | T.ImperativeTense): T.VerbTense | T.PerfectTense | T.ImperativeTense | T.ModalTense { 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.ModalTense : baseTense; return VS.tenseCategory === "modal" ? `${baseTense}Modal` as T.AbilityTense : baseTense;
} }
return <div> return <div>
<div className="clickable mb-2 small text-center" onClick={() => setShowFormulas(x => !x)}> <div className="clickable mb-2 small text-center" onClick={() => setShowFormulas(x => !x)}>

View File

@ -1,7 +1,7 @@
import Select from "react-select"; import Select from "react-select";
import * as T from "../../../types"; import * as T from "../../../types";
import ButtonSelect from "../ButtonSelect"; 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 useStickyState from "../useStickyState";
import { customStyles } from "../EntrySelect"; import { customStyles } from "../EntrySelect";
import { import {
@ -19,15 +19,15 @@ function composeFormula(formula: string, prefix: "passive" | "ability"): string
.replace("past participle", `${prefix} past participle`); .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 { 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.ModalTense | T.ImperativeTense; let tns: T.PerfectTense | T.VerbTense | T.AbilityTense | T.ImperativeTense;
const oldTenseCategory = !o const oldTenseCategory = !o
? undefined ? undefined
: getTenseCategory(o); : getTenseCategory(o);
const tenseOptions = oldTenseCategory === "perfect" const tenseOptions = oldTenseCategory === "perfect"
? perfectTenseOptions ? perfectTenseOptions
: oldTenseCategory === "modal" : 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" : oldTenseCategory === "imperative"
? imperativeTenseOptions ? imperativeTenseOptions
: verbTenseOptions; : verbTenseOptions;
@ -238,14 +238,14 @@ function TensePicker(props: ({
export default TensePicker; 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)) { if (isPerfectTense(tense)) {
return "perfect"; return "perfect";
} }
if (isVerbTense(tense)) { if (isVerbTense(tense)) {
return "basic"; return "basic";
} }
if (isModalTense(tense)) { if (isAbilityTense(tense)) {
return "modal"; return "modal";
} }
if (isImperativeTense(tense)) { if (isImperativeTense(tense)) {

View File

@ -6,7 +6,7 @@ import * as T from "../../../types";
function ChartDisplay({ conjugations, tense, opts, voice }: { function ChartDisplay({ conjugations, tense, opts, voice }: {
conjugations: T.VerbConjugation, conjugations: T.VerbConjugation,
tense: T.VerbTense | T.PerfectTense | T.ModalTense | T.ImperativeTense, tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense,
opts: T.TextOptions, opts: T.TextOptions,
voice: T.VerbSelection["voice"], voice: T.VerbSelection["voice"],
}) { }) {

View File

@ -12,7 +12,7 @@ import {
randomNumber, randomNumber,
} from "../lib/src/misc-helpers"; } from "../lib/src/misc-helpers";
import { entryFeeder } from "./entryFeeder"; 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"; import NPPronounPicker from "../components/src/np-picker/NPPronounPicker";
@ -56,7 +56,7 @@ const testPerfectTenses: T.PerfectTense[] = [
"pastSubjunctivePerfect", "pastSubjunctivePerfect",
]; ];
const testAbilityTenses: T.ModalTense[] = testVerbTenses.map<T.ModalTense>(t => `${t}Modal`); const testAbilityTenses: T.AbilityTense[] = testVerbTenses.map<T.AbilityTense>(t => `${t}Modal`);
const testTenses = [ const testTenses = [
...testVerbTenses, ...testVerbTenses,
@ -76,7 +76,7 @@ function VPBuilderDemo({ opts }: {
person: 0, person: 0,
}, "testPronoun"); }, "testPronoun");
const [testVoice, setTestVoice] = useStickyState<T.Voice>("active", "testVoice"); const [testVoice, setTestVoice] = useStickyState<T.Voice>("active", "testVoice");
const [testTense, setTestTense] = useStickyState<T.VerbTense | T.PerfectTense | T.ModalTense>("presentVerb", "testTense"); const [testTense, setTestTense] = useStickyState<T.VerbTense | T.PerfectTense | T.AbilityTense>("presentVerb", "testTense");
// const onlyGrammTrans = (arr: Transitivity[]) => ( // const onlyGrammTrans = (arr: Transitivity[]) => (
// arr.length === 1 && arr[0] === "grammatically transitive" // arr.length === 1 && arr[0] === "grammatically transitive"
// ); // );

View File

@ -10,10 +10,21 @@ export function makePsString(p: string, f: string): T.PsString {
return { p, f }; 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.DictionaryEntry): T.DictionaryEntryNoFVars;
export function removeFVarients(x: T.PsString): T.PsStringNoFVars; export function removeFVarients(x: T.PsString): T.PsStringNoFVars;
export function removeFVarients(x: string): T.FStringNoFVars; 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") { if (typeof x === "string") {
return x.split(",")[0] as T.FStringNoFVars; return x.split(",")[0] as T.FStringNoFVars;
} }

View File

@ -1,6 +1,6 @@
import * as T from "../../types"; import * as T from "../../types";
import { import {
isModalTense, isAbilityTense,
isPerfectTense, isPerfectTense,
isImperativeTense, isImperativeTense,
} from "./type-predicates"; } from "./type-predicates";
@ -24,7 +24,7 @@ function humanReadableVerbTense(tense: T.VerbTense): string {
: "habitual continuous past"; : "habitual continuous past";
} }
function humanReadableModalTense(tense: T.ModalTense): string { function humanReadableModalTense(tense: T.AbilityTense): string {
const base = tense.replace("Modal", "") as T.VerbTense; const base = tense.replace("Modal", "") as T.VerbTense;
return `${humanReadableVerbTense(base)} ability`; return `${humanReadableVerbTense(base)} ability`;
} }
@ -55,7 +55,7 @@ function humanReadableImperativeTense(tense: T.ImperativeTense): string {
} }
export function humanReadableVerbForm(f: T.VerbFormName): string { export function humanReadableVerbForm(f: T.VerbFormName): string {
return isModalTense(f) return isAbilityTense(f)
? humanReadableModalTense(f) ? humanReadableModalTense(f)
: isPerfectTense(f) : isPerfectTense(f)
? humanReadablePerfectTense(f) ? humanReadablePerfectTense(f)

View File

@ -1,7 +1,7 @@
import * as T from "../../types"; import * as T from "../../../types";
import { renderVerb } from "./render-verb"; import { renderVerb } from "./render-verb";
function vEntry(e: any, c?: any): T.VerbEntry { export function vEntry(e: any, c?: any): T.VerbEntry {
return { return {
entry: e, entry: e,
...c ? { ...c ? {
@ -1214,7 +1214,7 @@ test("perfect simple verb forms", () => {
}); });
test("ability simple verb forms", () => { test("ability simple verb forms", () => {
const tests: { verb: T.VerbEntry, tense: T.ModalTense, person: T.Person, output: ReturnType<typeof renderVerb> }[] = [ const tests: { verb: T.VerbEntry, tense: T.AbilityTense, person: T.Person, output: ReturnType<typeof renderVerb> }[] = [
{ {
tense: "presentVerbModal", tense: "presentVerbModal",
verb: ganul, verb: ganul,
@ -1625,7 +1625,7 @@ test("passive perfect simple verbs", () => {
}); });
test("passive ability simple verbs", () => { test("passive ability simple verbs", () => {
const tests: { verb: T.VerbEntry, tense: T.ModalTense, person: T.Person, output: ReturnType<typeof renderVerb> }[] = [ const tests: { verb: T.VerbEntry, tense: T.AbilityTense, person: T.Person, output: ReturnType<typeof renderVerb> }[] = [
{ {
verb: ganul, verb: ganul,
tense: "presentVerbModal", tense: "presentVerbModal",

View File

@ -1,47 +1,57 @@
import * as T from "../../types"; import * as T from "../../../types";
import { import {
functionOnOptLengths, functionOnOptLengths,
getPersonInflectionsKey, getPersonInflectionsKey,
getVerbBlockPosFromPerson, getVerbBlockPosFromPerson,
noPersInfs,
personGender, personGender,
personIsPlural, personIsPlural,
personNumber, personNumber,
} from "./misc-helpers"; } from "../misc-helpers";
import { import {
yulEndingInfinitive, yulEndingInfinitive,
} from "./p-text-helpers"; } from "../p-text-helpers";
import { import {
concatPsString, concatPsString,
getLength, getLength,
} from "./p-text-helpers"; } from "../p-text-helpers";
import { import {
presentEndings, presentEndings,
pastEndings, pastEndings,
equativeEndings, equativeEndings,
} from "./grammar-units"; } from "../grammar-units";
import { isKawulVerb, isModalTense, isPerfectTense, isTlulVerb } from "./type-predicates"; import { isKawulVerb, isAbilityTense, isPerfectTense, isTlulVerb } from "../type-predicates";
import { tenseHasBa } from "./phrase-building/vp-tools"; import { tenseHasBa } from "../phrase-building/vp-tools";
import { inflectYey } from "./pashto-inflector"; import { inflectYey } from "../pashto-inflector";
import { import {
getVerbInfo, getVerbInfo,
} from "./verb-info"; } from "../verb-info";
import { isPastTense } from "./phrase-building/vp-tools"; import { isPastTense } from "../phrase-building/vp-tools";
import { makePsString, removeFVarients } from "./accent-and-ps-utils"; import { makePsString, removeFVarients } from "../accent-and-ps-utils";
import { pashtoConsonants } from "./pashto-consonants"; import { pashtoConsonants } from "../pashto-consonants";
import { accentOnNFromEnd, removeAccents } from "./accent-helpers"; import { accentOnNFromEnd, removeAccents } from "../accent-helpers";
import { getRootStem as newGetRootStem } from "./roots-and-stems";
const kedulStatVerb: T.VerbEntry = { 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, 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: problem with laaR - no perfective split
// TODO: are azmóyulum and wáayulo really not just azmoyúlum and waayúlo ? // 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 // TODO: automatic 3rd person idiosyncronizing of raTul raaTu, shaRul, shaaRu, rasedul rased etc
export function renderVerb({ verb, tense, person, voice }: { export function renderVerb({ verb, tense, person, voice }: {
verb: T.VerbEntry, 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, person: T.Person,
voice: T.Voice, voice: T.Voice,
}): { }): {
@ -52,25 +62,36 @@ export function renderVerb({ verb, tense, person, voice }: {
if (isPerfectTense(tense)) { if (isPerfectTense(tense)) {
return getPerfectVBs({ verb, tense, person, voice }); return getPerfectVBs({ verb, tense, person, voice });
} }
const hasBa = tenseHasBa(tense); const hasBa = tenseHasBa(tense);
const isPast = isPastTense(tense); const isPast = isPastTense(tense);
const aspect = getAspect(tense); const aspect = getAspect(tense);
const isAbility = isModalTense(tense); const isAbility = isAbilityTense(tense);
const noPerfective = isAbility && (voice === "passive" || isTlulVerb(verb) || isKedul(verb)); 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({ const { perfectiveHead, rootStem } = getRootStem({
verb, aspect, isPast, isAbility, person, voice, noPerfective, verb, aspect, isPast, isAbility, person, voice, noPerfective,
}); });
const verbBlocks: T.VB[] = isAbility const verbBlocks: T.VB[] = isAbility
? getAbilityVerbBlocks({ isPast, person, root: rootStem, aspect, voice, noPerfective }) ? getAbilityVerbBlocks({ isPast, person, root: rootStem, aspect, voice, noPerfective })
: voice === "passive" : voice === "passive"
? getPassiveVerbBlocks({ root: rootStem, tense, person }) ? getPassiveVerbBlocks({ root: rootStem, tense, person })
: getBasicVerbBlock({ : getBasicVerbBlock({
rootStem, person, isPast, verb, aspect, rootStem, person, isPast, verb, aspect,
}); });
return { return {
hasBa, hasBa,
verbBlocks: [ verbBlocks: [
...!noPerfective && perfectiveHead ? [perfectiveHead] : [], ...(!noPerfective && perfectiveHead)
? [perfectiveHead] : [],
...verbBlocks, ...verbBlocks,
], ],
}; };
@ -185,19 +206,19 @@ function getPerfectVBs({ verb, tense, person, voice }: {
voice: T.Voice, voice: T.Voice,
}): { hasBa: boolean, verbBlocks: T.VB[] } { }): { hasBa: boolean, verbBlocks: T.VB[] } {
const hasBa = tenseHasBa(tense); 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") { if (voice === "passive") {
const [pt, eq] = getKedulStatPerfect(person, tense); const [pt, eq] = getKedulStatPerfect(person, tense);
const passiveRoot: T.VI = { const passiveRoot: T.VI = {
type: "VI", type: "VI",
ps: [noPersInfs(vInfo.root.imperfective).long], ps: [fromPersInfls(vInfo.root.imperfective, person).long],
}; };
const welded: T.Welded = weld(passiveRoot, pt); const welded: T.Welded = weld(passiveRoot, pt);
return { return {
hasBa, hasBa,
verbBlocks: [welded, eq], verbBlocks: [welded, eq],
} };
} }
const equative = equativeEndings[perfectTenseToEquative(tense)]; const equative = equativeEndings[perfectTenseToEquative(tense)];
@ -215,7 +236,7 @@ function getPerfectVBs({ verb, tense, person, voice }: {
type: "PT", type: "PT",
gender: personGender(person), gender: personGender(person),
number: personNumber(person), number: personNumber(person),
ps: chooseParticipleInflection(inflectYey(noPersInfs(vInfo.participle.past)), person) ps: chooseParticipleInflection(inflectYey(fromPersInfls(vInfo.participle.past, person)), person)
} }
return { return {
hasBa, hasBa,
@ -246,13 +267,13 @@ function getRootStem({ verb, aspect, isPast, isAbility, voice, person, noPerfect
perfectiveHead: undefined | T.PH, perfectiveHead: undefined | T.PH,
rootStem: T.SingleOrLengthOpts<T.PsString>, rootStem: T.SingleOrLengthOpts<T.PsString>,
} { } {
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"]; const rs = vInfo[(isPast || isAbility || voice === "passive") ? "root" : "stem"];
if (noPerfective) { if (noPerfective) {
// exception with tlul verbs for ability stems // exception with tlul verbs for ability stems
return { return {
perfectiveHead: undefined, perfectiveHead: undefined,
rootStem: noPersInfs(rs.imperfective), rootStem: fromPersInfls(rs.imperfective, person),
}; };
} }
if (aspect === "perfective" && rs.perfectiveSplit) { if (aspect === "perfective" && rs.perfectiveSplit) {
@ -262,7 +283,7 @@ function getRootStem({ verb, aspect, isPast, isAbility, voice, person, noPerfect
perfectiveHead: undefined, perfectiveHead: undefined,
// because the persInfs only happen with stative compound verbs,j // because the persInfs only happen with stative compound verbs,j
// which we are handling differently now // which we are handling differently now
rootStem: noPersInfs(rs[aspect]), rootStem: fromPersInfls(rs[aspect], person),
} }
function extractPerfectiveSplit(splitInfo: T.SplitInfo): ReturnType<typeof getRootStem> { function extractPerfectiveSplit(splitInfo: T.SplitInfo): ReturnType<typeof getRootStem> {
@ -438,7 +459,7 @@ function ensure3rdPast(ending: T.PsString[], rs: T.PsString, verb: T.VerbEntry,
] : ending).map(e => concatPsString(rs, e)); ] : 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", ""); const t = tense.replace("Modal", "");
if (["presentVerb", "imperfectiveFuture", "imperfectivePast", "habitualImperfectivePast"].includes(t)) { if (["presentVerb", "imperfectiveFuture", "imperfectivePast", "habitualImperfectivePast"].includes(t)) {
return "imperfective"; return "imperfective";

View File

@ -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<typeof getRootStem>;
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);
});
});

View File

@ -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<T.PsString[]>)[];
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),
};
}

View File

@ -88,7 +88,7 @@ export function renderEnglishVPBase({ subjectPerson, object, vs }: {
]), ]),
}; };
const modalBuilders: Record< const modalBuilders: Record<
T.ModalTense, T.AbilityTense,
(s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => string[] (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => string[]
> = { > = {
presentVerbModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ presentVerbModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([
@ -215,7 +215,7 @@ export function renderEnglishVPBase({ subjectPerson, object, vs }: {
]), ]),
} }
const passiveModalBuilders: Record< const passiveModalBuilders: Record<
T.ModalTense, T.AbilityTense,
(s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => string[] (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => string[]
> = { > = {
presentVerbModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ presentVerbModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([

View File

@ -21,7 +21,7 @@ import {
isAdjectiveEntry, isAdjectiveEntry,
isImperativeTense, isImperativeTense,
isLocativeAdverbEntry, isLocativeAdverbEntry,
isModalTense, isAbilityTense,
isNounEntry, isNounEntry,
isPattern4Entry, isPattern4Entry,
isPerfectTense, isPerfectTense,
@ -505,7 +505,7 @@ export function isStativeHelper(v: T.VerbEntry): boolean {
} }
function splitUpIfModal(v: T.VerbRenderedBlock): [T.VerbRenderedBlock] | [T.ModalVerbBlock, T.ModalVerbKedulPart] { function splitUpIfModal(v: T.VerbRenderedBlock): [T.VerbRenderedBlock] | [T.ModalVerbBlock, T.ModalVerbKedulPart] {
if (!isModalTense(v.block.tense)) { if (!isAbilityTense(v.block.tense)) {
return [v]; return [v];
} }
const [vrb, k] = splitOffLeapfrogWordFull(v.block.ps); 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]); const hasBa = hasBaParticle(getLong(verbForm)[0]);
if (perfective) { if (perfective) {
const past = isPastTense(vs.tense); const past = isPastTense(vs.tense);
const splitInfo = conj.info[(past || isModalTense(vs.tense)) ? "root" : "stem"].perfectiveSplit; const splitInfo = conj.info[(past || isAbilityTense(vs.tense)) ? "root" : "stem"].perfectiveSplit;
if (!splitInfo || (isTlulVerb(vs.verb.entry) && isModalTense(vs.tense))) { if (!splitInfo || (isTlulVerb(vs.verb.entry) && isAbilityTense(vs.tense))) {
return { ps: { head: undefined, rest: removeBaFromForm(verbForm) }, hasBa }; return { ps: { head: undefined, rest: removeBaFromForm(verbForm) }, hasBa };
} }
// TODO: Either solve this in the inflector or here, it seems silly (or redundant) // TODO: Either solve this in the inflector or here, it seems silly (or redundant)

View File

@ -38,7 +38,7 @@ export function isInvalidSubjObjCombo(subj: T.Person, obj: T.Person): boolean {
*/ */
export function getTenseVerbForm( export function getTenseVerbForm(
conjR: T.VerbConjugation, conjR: T.VerbConjugation,
tense: T.VerbTense | T.PerfectTense | T.ModalTense | T.ImperativeTense, tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense,
voice: "active" | "passive", voice: "active" | "passive",
mode: "charts" | "phrase-building", mode: "charts" | "phrase-building",
negative: boolean, negative: boolean,
@ -152,8 +152,8 @@ export function removeBa(ps: T.PsString): T.PsString {
return psRemove(ps, concatPsString(grammarUnits.baParticle, " ")); return psRemove(ps, concatPsString(grammarUnits.baParticle, " "));
} }
export function getTenseFromVerbSelection(vs: T.VerbSelection): T.VerbTense | T.PerfectTense | T.ModalTense | T.ImperativeTense { export function getTenseFromVerbSelection(vs: T.VerbSelection): T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense {
function verbTenseToModalTense(tn: T.VerbTense): T.ModalTense { function verbTenseToModalTense(tn: T.VerbTense): T.AbilityTense {
if (tn === "presentVerb") { if (tn === "presentVerb") {
return "presentVerbModal"; return "presentVerbModal";
} }
@ -198,7 +198,7 @@ export function isPastTense(tense: T.Tense): boolean {
return tense.toLowerCase().includes("past"); 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 [ return [
"imperfectiveFuture", "imperfectiveFuture",
"perfectiveFuture", "perfectiveFuture",

View File

@ -222,7 +222,7 @@ export function isVerbTense(tense: T.Tense): tense is T.VerbTense {
return verbTenses.some(x => x === tense); 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"); return tense.endsWith("Modal");
} }

View File

@ -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 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 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" }; 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 EquativeTense = "present" | "subjunctive" | "habitual" | "past" | "future" | "wouldBe" | "pastSubjunctive" | "wouldHaveBeen";
export type PerfectTense = `${EquativeTense}Perfect`; export type PerfectTense = `${EquativeTense}Perfect`;
export type ModalTense = `${VerbTense}Modal`; export type AbilityTense = `${VerbTense}Modal`;
export type ImperativeTense = `${Aspect}Imperative`; export type ImperativeTense = `${Aspect}Imperative`;
export type Tense = EquativeTense | VerbTense | PerfectTense | ModalTense | ImperativeTense; export type Tense = EquativeTense | VerbTense | PerfectTense | AbilityTense | ImperativeTense;
export type SubjectSelection = { export type SubjectSelection = {
type: "subjectSelection", type: "subjectSelection",
@ -655,7 +660,7 @@ export type VPSelectionComplete = {
form: FormVersion, form: FormVersion,
}; };
export type VerbFormName = VerbTense | PerfectTense | ModalTense | ImperativeTense; export type VerbFormName = VerbTense | PerfectTense | AbilityTense | ImperativeTense;
export type VerbSelectionComplete = Omit<VerbSelection, "object" | "verbTense" | "perfectTense" | "imperativeTense" | "tenseCategory"> & { export type VerbSelectionComplete = Omit<VerbSelection, "object" | "verbTense" | "perfectTense" | "imperativeTense" | "tenseCategory"> & {
tense: VerbFormName, tense: VerbFormName,
@ -1075,6 +1080,9 @@ export type VB = PH | VA | VI | PT | EQ | Welded;
export type PH = { export type PH = {
type: "PH", type: "PH",
ps: PsString, ps: PsString,
} | {
type: "PHComp",
comp: Comp,
}; };
/** verb block with person agreement */ /** verb block with person agreement */
@ -1105,11 +1113,30 @@ export type EQ = {
person: Person, 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 */ /** a veb block with a welded part having it's accents removed */
export type Welded = { export type Welded = {
type: "welded", type: "welded",
/** accents must be removed from the left */ /** accents must be removed from the left */
left: VI, // TODO - will get more complex with compounds left: VI | Comp,
right: VA | PT | VI, right: VA | PT | VI,
}; };