lots more towards the new more correct and concise verb conjugator
This commit is contained in:
parent
68189ea4ba
commit
c9e3d13c43
11
src/App.tsx
11
src/App.tsx
|
@ -21,7 +21,6 @@ import { entryFeeder } from "./demo-components/entryFeeder";
|
|||
import { Hider } from "./components/library";
|
||||
import InflectionDemo from "./demo-components/InflectionDemo";
|
||||
import SpellingDemo from "./demo-components/SpellingDemo";
|
||||
import { renderVerb } from "./lib/src/render-verb";
|
||||
|
||||
function App() {
|
||||
const [showingTextOptions, setShowingTextOptions] = useStickyState<boolean>(false, "showTextOpts1");
|
||||
|
@ -36,17 +35,9 @@ function App() {
|
|||
useEffect(() => {
|
||||
document.documentElement.setAttribute("data-theme", theme);
|
||||
}, [theme]);
|
||||
const rv = renderVerb({
|
||||
verb: { entry: {"ts":1527815399,"i":15035,"p":"وهل","f":"wahul","g":"wahul","e":"to hit","r":4,"c":"v. trans.","tppp":"واهه","tppf":"waahu","ec":"hit,hits,hitting,hit,hit"} as T.VerbDictionaryEntry},
|
||||
aspect: "imperfective",
|
||||
tense: "habitualPast",
|
||||
person: 9,
|
||||
});
|
||||
|
||||
return <>
|
||||
<main className="flex-shrink-0 mb-4">
|
||||
<pre>
|
||||
{JSON.stringify(rv, null, " ")}
|
||||
</pre>
|
||||
<div className="container" style={{ maxWidth: "800px" }}>
|
||||
<div style={{ position: "absolute", top: "1.5rem", right: "1.5rem", display: "flex", flexDirection: "row" }}>
|
||||
<div
|
||||
|
|
|
@ -31,7 +31,6 @@ function Examples(props: ({
|
|||
opts: T.TextOptions,
|
||||
lineHeight?: 0 | 1 | 2 | 3 | 4,
|
||||
}) {
|
||||
console.log({ props });
|
||||
const examples = "children" in props ? props.children : props.ex;
|
||||
const Example = ({ children: text }: { children: PsStringWSub }) => (
|
||||
<div className={props.lineHeight !== undefined ? `mb-${props.lineHeight}` : `mt-1 mb-3`}>
|
||||
|
|
|
@ -12,6 +12,8 @@ import {
|
|||
randomNumber,
|
||||
} from "../lib/src/misc-helpers";
|
||||
import { entryFeeder } from "./entryFeeder";
|
||||
import { renderVerb } from "../lib/src/render-verb";
|
||||
import NPPronounPicker from "../components/src/np-picker/NPPronounPicker";
|
||||
|
||||
|
||||
const transitivities: T.Transitivity[] = [
|
||||
|
@ -32,12 +34,46 @@ const verbTypes: VerbType[] = [
|
|||
"dynamic compound",
|
||||
];
|
||||
|
||||
const testVerbTenses: T.VerbTense[] = [
|
||||
"presentVerb",
|
||||
"subjunctiveVerb",
|
||||
"imperfectiveFuture",
|
||||
"perfectiveFuture",
|
||||
"imperfectivePast",
|
||||
"perfectivePast",
|
||||
"habitualImperfectivePast",
|
||||
"habitualPerfectivePast",
|
||||
];
|
||||
|
||||
const testPerfectTenses: T.PerfectTense[] = [
|
||||
"presentPerfect",
|
||||
"pastPerfect",
|
||||
"subjunctivePerfect",
|
||||
"wouldBePerfect",
|
||||
"wouldHaveBeenPerfect",
|
||||
];
|
||||
|
||||
const testAbilityTenses: T.ModalTense[] = testVerbTenses.map<T.ModalTense>(t => `${t}Modal`);
|
||||
|
||||
const testTenses = [
|
||||
...testVerbTenses,
|
||||
...testPerfectTenses,
|
||||
...testAbilityTenses,
|
||||
];
|
||||
|
||||
function VPBuilderDemo({ opts }: {
|
||||
opts: T.TextOptions,
|
||||
}) {
|
||||
const [verbTs, setVerbTs] = useStickyState<number>(0, "verbTs1");
|
||||
const [verbTypeShowing, setVerbTypeShowing] = useStickyState<VerbType>("simple", "vTypeShowing");
|
||||
const [transitivityShowing, setTransitivityShowing] = useStickyState<T.Transitivity>("intransitive", "transitivityShowing1");
|
||||
const [testPerson, setTestPerson] = useStickyState<T.PronounSelection>({
|
||||
type: "pronoun",
|
||||
distance: "far",
|
||||
person: 0,
|
||||
}, "testPronoun");
|
||||
const [testVoice, setTestVoice] = useStickyState<T.Voice>("active", "testVoice");
|
||||
const [testTense, setTestTense] = useStickyState<T.VerbTense | T.PerfectTense | T.ModalTense>("presentVerb", "testTense");
|
||||
// const onlyGrammTrans = (arr: Transitivity[]) => (
|
||||
// arr.length === 1 && arr[0] === "grammatically transitive"
|
||||
// );
|
||||
|
@ -105,6 +141,14 @@ function VPBuilderDemo({ opts }: {
|
|||
const makeVerbLabel = (entry: T.DictionaryEntry): string => (
|
||||
`${entry.p} - ${clamp(entry.e, 20)}`
|
||||
);
|
||||
const rv = v ? renderVerb({
|
||||
// verb: { entry: {"ts":1527815399,"i":15035,"p":"وهل","f":"wahul","g":"wahul","e":"to hit","r":4,"c":"v. trans.","tppp":"واهه","tppf":"waahu","ec":"hit,hits,hitting,hit,hit"} as T.VerbDictionaryEntry},
|
||||
// verb: { entry: {"ts":1527814596,"i":8648,"p":"شرمول","f":"shărmawul","g":"sharmawul","e":"to shame, to disgrace, to dishonor, to embarrass","r":4,"c":"v. trans.","ec":"embarrass"} as T.VerbDictionaryEntry },
|
||||
verb: v.verb as T.VerbEntry,
|
||||
tense: testTense,
|
||||
person: testPerson.person,
|
||||
voice: testVoice,
|
||||
}) : undefined;
|
||||
return <div className="mt-4">
|
||||
<div className="d-block mx-auto card" style={{ maxWidth: "700px", background: "var(--closer)"}}>
|
||||
<div className="card-body">
|
||||
|
@ -189,6 +233,23 @@ function VPBuilderDemo({ opts }: {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button onClick={() => setTestVoice(v => v === "active" ? "passive" : "active")}>
|
||||
{testVoice}
|
||||
</button>
|
||||
<select value={testTense} onChange={e => setTestTense(e.target.value as any)}>
|
||||
{testTenses.map(t => (
|
||||
<option key={t} value={t}>{t}</option>
|
||||
))}
|
||||
</select>
|
||||
<NPPronounPicker
|
||||
onChange={setTestPerson}
|
||||
pronoun={testPerson}
|
||||
role="subject"
|
||||
opts={opts}
|
||||
/>
|
||||
<pre>
|
||||
{JSON.stringify(rv, null, " ")}
|
||||
</pre>
|
||||
{v?.verb.entry && <div style={{ paddingBottom: "20px" }}>
|
||||
<PhraseBuilder
|
||||
handleLinkClick="none"
|
||||
|
|
|
@ -46,6 +46,9 @@ import {
|
|||
getLength,
|
||||
psStringEquals,
|
||||
} from "./src/p-text-helpers";
|
||||
import {
|
||||
joiningTails,
|
||||
} from "./src/misc-text";
|
||||
import {
|
||||
getEnglishWord,
|
||||
} from "./src/get-english-word";
|
||||
|
@ -249,6 +252,7 @@ export {
|
|||
makeVPSelectionState,
|
||||
getLength,
|
||||
psStringEquals,
|
||||
joiningTails,
|
||||
blockUtils,
|
||||
blank,
|
||||
kidsBlank,
|
||||
|
|
|
@ -679,26 +679,26 @@ const baseInflectedPronouns = [
|
|||
|
||||
const plainPronounsFar = [
|
||||
...basePlainPronouns,
|
||||
[[{ p: "هغه", f: "haghá" }], [{ p: "هغوي", f: "haghwée" }]],
|
||||
[[{ p: "هغه", f: "haghá" }], [{ p: "هغوي", f: "haghwée" }]],
|
||||
[[{ p: "هغه", f: "haghá" }], [{ p: "هغوی", f: "haghwée" }]],
|
||||
[[{ p: "هغه", f: "haghá" }], [{ p: "هغوی", f: "haghwée" }]],
|
||||
] as T.VerbBlock;
|
||||
|
||||
const plainPronounsNear = [
|
||||
...basePlainPronouns,
|
||||
[[{ p: "دی", f: "dey" }], [{ p: "دوي", f: "dwee" }]],
|
||||
[[{ p: "دا", f: "daa" }], [{ p: "دوي", f: "dwee" }]],
|
||||
[[{ p: "دی", f: "dey" }], [{ p: "دوی", f: "dwee" }]],
|
||||
[[{ p: "دا", f: "daa" }], [{ p: "دوی", f: "dwee" }]],
|
||||
] as T.VerbBlock;
|
||||
|
||||
const inflectedPronounsFar = [
|
||||
...baseInflectedPronouns,
|
||||
[[{ p: "هغهٔ", f: "haghú" }], [{ p: "هغوي", f: "haghwée" }]],
|
||||
[[{ p: "هغې", f: "haghé" }], [{ p: "هغوي", f: "haghwée" }]],
|
||||
[[{ p: "هغهٔ", f: "haghú" }], [{ p: "هغوی", f: "haghwée" }]],
|
||||
[[{ p: "هغې", f: "haghé" }], [{ p: "هغوی", f: "haghwée" }]],
|
||||
] as T.VerbBlock;
|
||||
|
||||
const inflectedPronounsNear = [
|
||||
...baseInflectedPronouns,
|
||||
[[{ p: "دهٔ", f: "du" }], [{ p: "دوي", f: "dwee" }]],
|
||||
[[{ p: "دې", f: "de" }], [{ p: "دوي", f: "dwee" }]],
|
||||
[[{ p: "دهٔ", f: "du" }], [{ p: "دوی", f: "dwee" }]],
|
||||
[[{ p: "دې", f: "de" }], [{ p: "دوی", f: "dwee" }]],
|
||||
] as T.VerbBlock;
|
||||
|
||||
const miniPronouns: T.VerbBlock = [
|
||||
|
|
|
@ -96,6 +96,20 @@ export function hasPersInfs(info: T.NonComboVerbInfo | T.PassiveRootsAndStems |
|
|||
);
|
||||
}
|
||||
|
||||
export function functionOnOptLengths<U extends object, F extends object>(x: T.SingleOrLengthOpts<U>, f: (y: U) => F): T.SingleOrLengthOpts<F> {
|
||||
if ("long" in x) {
|
||||
return {
|
||||
long: f(x.long),
|
||||
short: f(x.short),
|
||||
...("mini" in x && x.mini) ? {
|
||||
mini: f(x.mini),
|
||||
} : {},
|
||||
};
|
||||
}
|
||||
return f(x);
|
||||
}
|
||||
|
||||
// TODO: deprecated using new verb rendering thing
|
||||
export function chooseParticipleInflection(
|
||||
pPartInfs: T.SingleOrLengthOpts<T.UnisexInflections> | T.SingleOrLengthOpts<T.PsString>,
|
||||
person: T.Person,
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
import { PsString } from "../../types";
|
||||
|
||||
export const joiningTails: PsString = { p: "ـ", f: "–"};
|
|
@ -451,7 +451,7 @@ export function concatInflections(
|
|||
comp: T.PsString | T.SingleOrLengthOpts<T.UnisexInflections>, infs: T.SingleOrLengthOpts<T.UnisexInflections>
|
||||
): T.SingleOrLengthOpts<T.UnisexInflections> {
|
||||
const containsLengthOptions = "long" in infs || "long" in comp;
|
||||
const ensureL = <T>(x: T.SingleOrLengthOpts<T>, length: "short" | "long"): T => (
|
||||
const ensureL = <T extends object>(x: T.SingleOrLengthOpts<T>, length: "short" | "long"): T => (
|
||||
("long" in x) ? x[length] : x
|
||||
);
|
||||
if (containsLengthOptions) {
|
||||
|
@ -509,7 +509,7 @@ export function allOnePersonInflection(
|
|||
return block;
|
||||
}
|
||||
|
||||
export function choosePersInf<T>(x: T.FullForm<T>, persInf: T.PersonInflectionsField): T.SingleOrLengthOpts<T> {
|
||||
export function choosePersInf<T extends object>(x: T.FullForm<T>, persInf: T.PersonInflectionsField): T.SingleOrLengthOpts<T> {
|
||||
if ("mascSing" in x) {
|
||||
return x[persInf];
|
||||
}
|
||||
|
@ -999,7 +999,7 @@ export function psStringFromEntry(entry: T.PsString): T.PsString {
|
|||
};
|
||||
}
|
||||
|
||||
export function getLength<U>(x: T.SingleOrLengthOpts<U>, length: "long" | "short" | "mini"): U {
|
||||
export function getLength<U extends object>(x: T.SingleOrLengthOpts<U>, length: "long" | "short" | "mini"): U {
|
||||
if ("long" in x) {
|
||||
const s = x[length];
|
||||
return s ? s : x.short;
|
||||
|
@ -1007,14 +1007,14 @@ export function getLength<U>(x: T.SingleOrLengthOpts<U>, length: "long" | "short
|
|||
return x;
|
||||
}
|
||||
|
||||
export function getLong<U>(x: T.SingleOrLengthOpts<U>): U {
|
||||
export function getLong<U extends object>(x: T.SingleOrLengthOpts<U>): U {
|
||||
if ("long" in x) {
|
||||
return x.long;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
export function getShort<U>(a: T.SingleOrLengthOpts<U>): U {
|
||||
export function getShort<U extends object>(a: T.SingleOrLengthOpts<U>): U {
|
||||
if ("long" in a) {
|
||||
return a.short;
|
||||
}
|
||||
|
|
|
@ -198,6 +198,22 @@ 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 {
|
||||
return [
|
||||
"imperfectiveFuture",
|
||||
"perfectiveFuture",
|
||||
"habitualPerfectivePast",
|
||||
"habitualImperfectivePast",
|
||||
"imperfectiveFutureModal",
|
||||
"perfectiveFutureModal",
|
||||
"habitualPerfectivePastModal",
|
||||
"habitualImperfectivePastModal",
|
||||
"futurePerfect",
|
||||
"wouldBePerfect",
|
||||
"wouldBeHaveBeenPerfect",
|
||||
].includes(tense);
|
||||
}
|
||||
|
||||
export function removeDuplicates(psv: T.PsString[]): T.PsString[] {
|
||||
return psv.filter((ps, i, arr) => (
|
||||
i === arr.findIndex(t => (
|
||||
|
|
|
@ -1,13 +1,38 @@
|
|||
import {
|
||||
functionOnOptLengths,
|
||||
getPersonInflectionsKey,
|
||||
getVerbBlockPosFromPerson,
|
||||
noPersInfs,
|
||||
personGender,
|
||||
personIsPlural,
|
||||
} from "./misc-helpers";
|
||||
import {
|
||||
yulEndingInfinitive,
|
||||
} from "./p-text-helpers";
|
||||
import * as T from "../../types";
|
||||
import { concatPsString, getVerbInfo } from "../library";
|
||||
import {
|
||||
concatPsString,
|
||||
getLength,
|
||||
} 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";
|
||||
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";
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
// export type RenderedVerbB = VerbRenderedBlock
|
||||
// | PerfectiveHeadBlock
|
||||
|
@ -27,47 +52,111 @@ import {
|
|||
// },
|
||||
// };
|
||||
|
||||
type PerfectiveHeadBlock = {
|
||||
type: "perfectiveHead",
|
||||
// TODO the welded block with passive is the same as the stative compounds
|
||||
|
||||
type VB = PH | VA | VPlain | PT | EQ | Welded;
|
||||
|
||||
type PH = {
|
||||
type: "perfectiveHeadBlock",
|
||||
ps: T.PsString,
|
||||
};
|
||||
|
||||
type VerbBlock = {
|
||||
type: "verb",
|
||||
hasBa: boolean,
|
||||
type VA = {
|
||||
type: "verbBlockWithAgreement",
|
||||
ps: T.SingleOrLengthOpts<T.PsString[]>,
|
||||
person: T.Person,
|
||||
aspect: T.Aspect,
|
||||
tense: keyof T.AspectContent,
|
||||
};
|
||||
|
||||
export function renderVerb({ verb, aspect, tense, person }: {
|
||||
verb: T.VerbEntry,
|
||||
aspect: T.Aspect,
|
||||
tense: keyof T.AspectContent,
|
||||
type VPlain = {
|
||||
type: "verbBlockWithoutAgreement",
|
||||
ps: T.SingleOrLengthOpts<T.PsString[]>,
|
||||
};
|
||||
|
||||
type PT = {
|
||||
type: "participleBlock",
|
||||
ps: T.SingleOrLengthOpts<T.PsString[]>,
|
||||
inflection: T.PersonInflectionsField,
|
||||
};
|
||||
|
||||
type EQ = {
|
||||
type: "equativeBlock",
|
||||
ps: T.SingleOrLengthOpts<T.PsString[]>,
|
||||
person: T.Person,
|
||||
};
|
||||
|
||||
type Welded = {
|
||||
type: "weldedBlock",
|
||||
left: VPlain, // TODO - will get more complex with compounds
|
||||
right: VA | PT | VPlain,
|
||||
}
|
||||
|
||||
// TODO: problem with laaR - no perfective split
|
||||
|
||||
export function renderVerb({ verb, tense, person, voice }: {
|
||||
verb: T.VerbEntry,
|
||||
tense: T.VerbTense | T.PerfectTense | T.ModalTense, // TODO: make T.Tense
|
||||
person: T.Person,
|
||||
voice: T.Voice,
|
||||
}): {
|
||||
hasBa: boolean,
|
||||
verbBlocks: [PerfectiveHeadBlock, VerbBlock] | [VerbBlock]
|
||||
verbBlocks: VB[],
|
||||
} {
|
||||
// WARNING: this only works with simple verbs
|
||||
const isPast = tense === "past" || tense === "habitualPast";
|
||||
const hasBa = tense === "future" || tense === "habitualPast";
|
||||
const { perfectiveHead, rootStem } = getRootStem(verb, aspect, isPast);
|
||||
const ending = getEnding(person, isPast);
|
||||
const verbPs = addEnding(rootStem, ending);
|
||||
const verbBlock: VerbBlock = {
|
||||
type: "verb",
|
||||
const hasBa = tenseHasBa(tense);
|
||||
if (isPerfectTense(tense)) {
|
||||
return {
|
||||
hasBa,
|
||||
ps: verbPs,
|
||||
person,
|
||||
aspect,
|
||||
tense,
|
||||
verbBlocks: getPerfectBlocks({ verb, tense, person, voice }),
|
||||
};
|
||||
const perfectiveHeadBlock: PerfectiveHeadBlock | undefined = perfectiveHead ? {
|
||||
type: "perfectiveHead",
|
||||
ps: noPersInfs(perfectiveHead),
|
||||
}
|
||||
const isPast = isPastTense(tense);
|
||||
const aspect = getAspect(tense);
|
||||
const isAbility = isModalTense(tense);
|
||||
const { perfectiveHead, rootStem } = getRootStem({
|
||||
verb, aspect, isPast, isAbility, person, voice
|
||||
});
|
||||
const perfectiveHeadBlock: PH | undefined = perfectiveHead ? {
|
||||
type: "perfectiveHeadBlock",
|
||||
// should only need this for tlul and Daaredul?
|
||||
ps: fromPersInfls(perfectiveHead, person),
|
||||
} : undefined;
|
||||
// if (voice === "passive") {
|
||||
// const kedulPart = getPassiveVerbBlocks(tense, person, aspect, rootStem);
|
||||
// return {
|
||||
// hasBa,
|
||||
// // @ts-ignore
|
||||
// verbBlocks: perfectiveHeadBlock
|
||||
// ? [perfectiveHeadBlock, kedulPart]
|
||||
// : [kedulPart],
|
||||
// }
|
||||
// }
|
||||
const ending = getEnding(person, isPast);
|
||||
if (isAbility) {
|
||||
const [vb, shPart] = getAbilityVerbBlocks({ verb, isPast, person, rootStem, aspect, voice });
|
||||
return {
|
||||
hasBa,
|
||||
verbBlocks: perfectiveHeadBlock
|
||||
? [perfectiveHeadBlock, vb, shPart]
|
||||
: [vb, shPart],
|
||||
};
|
||||
}
|
||||
if (voice === "passive") {
|
||||
const vbs = getPassiveVerbBlocks({ root: rootStem, tense, person });
|
||||
return {
|
||||
hasBa,
|
||||
verbBlocks: [
|
||||
...perfectiveHeadBlock ? [perfectiveHeadBlock] : [],
|
||||
vbs,
|
||||
],
|
||||
};
|
||||
}
|
||||
const verbBlock: VA = {
|
||||
type: "verbBlockWithAgreement",
|
||||
ps: addEnding({
|
||||
rootStem, ending, person, isPast, verb, aspect,
|
||||
}),
|
||||
person,
|
||||
};
|
||||
return {
|
||||
hasBa,
|
||||
verbBlocks: perfectiveHeadBlock ? [
|
||||
|
@ -76,45 +165,151 @@ export function renderVerb({ verb, aspect, tense, person }: {
|
|||
}
|
||||
}
|
||||
|
||||
function addEnding(rootStem: T.FullForm<T.PsString>, ending: T.SingleOrLengthOpts<T.PsString[]>): T.SingleOrLengthOpts<T.PsString[]> {
|
||||
const rs = noPersInfs(rootStem);
|
||||
const end = noPersInfs(ending);
|
||||
if ("long" in rs) {
|
||||
if ("long" in end) {
|
||||
return {
|
||||
long: end.long.map((e) => concatPsString(rs.long, e)),
|
||||
short: end.short.map((e) => concatPsString(rs.short, e)),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
long: end.map((e) => concatPsString(rs.long, e)),
|
||||
short: end.map((e) => concatPsString(rs.short, e)),
|
||||
};
|
||||
function getPassiveVerbBlocks({ root, tense, person }: {
|
||||
root: T.SingleOrLengthOpts<T.PsString>,
|
||||
tense: T.VerbTense,
|
||||
person: T.Person,
|
||||
}): Welded {
|
||||
if (!("long" in root)) {
|
||||
throw new Error("should have length versions in roots for passive");
|
||||
}
|
||||
}
|
||||
if ("long" in end) {
|
||||
throw new Error("should not be using verb stems with long and short endings");
|
||||
}
|
||||
return end.map((e) => concatPsString(rs, e));
|
||||
const { verbBlocks: [auxVerb] } = renderVerb({
|
||||
verb: kedulStatVerb,
|
||||
tense,
|
||||
person,
|
||||
voice: "active",
|
||||
}) as { hasBa: boolean, verbBlocks: [VA] };
|
||||
return weld(
|
||||
{
|
||||
type: "verbBlockWithoutAgreement",
|
||||
ps: [root.long],
|
||||
},
|
||||
auxVerb,
|
||||
);
|
||||
}
|
||||
|
||||
function getRootStem(verb: T.VerbEntry, aspect: T.Aspect, isPast: boolean): {
|
||||
function getAbilityVerbBlocks({ verb, isPast, person, rootStem, aspect, voice }: {
|
||||
verb: T.VerbEntry,
|
||||
isPast: boolean,
|
||||
person: T.Person,
|
||||
rootStem: T.SingleOrLengthOpts<T.PsString>,
|
||||
aspect: T.Aspect,
|
||||
voice: T.Voice,
|
||||
}): VB[] {
|
||||
const noPerfective = isTlulVerb(verb) || isKedul(verb);
|
||||
const shBlock = getAbilityShPart(isPast, person);
|
||||
// TODO: this is redundant, we did it in another part of the program?
|
||||
const verbBlock: VPlain = {
|
||||
type: "verbBlockWithoutAgreement",
|
||||
ps: addAbilityTailsToRs(rootStem, aspect, noPerfective),
|
||||
};
|
||||
return [verbBlock, shBlock];
|
||||
function getAbilityShPart(isPast: boolean, person: T.Person): VA {
|
||||
// TODO: optimized shortcut version of this
|
||||
const { verbBlocks: [shBlock] } = renderVerb({
|
||||
verb: kedulStatVerb,
|
||||
tense: isPast ? "perfectivePast" : "subjunctiveVerb",
|
||||
person,
|
||||
voice: "active",
|
||||
}) as {
|
||||
hasBa: boolean,
|
||||
verbBlocks: [VA],
|
||||
};
|
||||
return {
|
||||
type: "verbBlockWithAgreement",
|
||||
ps: shBlock.ps,
|
||||
person,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getPerfectBlocks({ verb, tense, person, voice }: {
|
||||
verb: T.VerbEntry,
|
||||
tense: T.PerfectTense,
|
||||
person: T.Person,
|
||||
voice: T.Voice,
|
||||
}): VB[] {
|
||||
const vInfo = getVerbInfo(verb.entry) as T.SimpleVerbInfo;
|
||||
|
||||
// TODO: put this in a seperate function?
|
||||
|
||||
if (voice === "passive") {
|
||||
const [pt, eq] = getKedulStatPerfect(person, tense);
|
||||
const passiveRoot: VPlain = {
|
||||
type: "verbBlockWithoutAgreement",
|
||||
ps: [noPersInfs(vInfo.root.imperfective).long],
|
||||
};
|
||||
const welded: Welded = weld(passiveRoot, pt);
|
||||
return [welded, eq];
|
||||
}
|
||||
|
||||
const equative = equativeEndings[perfectTenseToEquative(tense)];
|
||||
const [row, col] = getVerbBlockPosFromPerson(person);
|
||||
const equativeBlock: EQ = {
|
||||
type: "equativeBlock",
|
||||
person,
|
||||
ps: "long" in equative ? {
|
||||
long: equative.long[row][col],
|
||||
short: equative.short[row][col],
|
||||
} : equative[row][col],
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const participleBlock: PT = {
|
||||
type: "participleBlock",
|
||||
inflection: getPersonInflectionsKey(person),
|
||||
ps: chooseParticipleInflection(inflectYey(noPersInfs(vInfo.participle.past)), person)
|
||||
}
|
||||
return [participleBlock, equativeBlock];
|
||||
}
|
||||
|
||||
function weld(left: VPlain, right: VA | PT | VPlain): Welded {
|
||||
return {
|
||||
type: "weldedBlock",
|
||||
left: {
|
||||
...left,
|
||||
ps: functionOnOptLengths(left.ps, removeAccents),
|
||||
},
|
||||
right,
|
||||
};
|
||||
}
|
||||
|
||||
function getRootStem({ verb, aspect, isPast, isAbility, voice, person }: {
|
||||
verb: T.VerbEntry,
|
||||
aspect: T.Aspect,
|
||||
isPast: boolean,
|
||||
isAbility: boolean,
|
||||
person: T.Person,
|
||||
voice: T.Voice,
|
||||
}): {
|
||||
perfectiveHead: undefined | T.OptionalPersonInflections<T.PsString>,
|
||||
rootStem: T.OptionalPersonInflections<T.SingleOrLengthOpts<T.PsString>>,
|
||||
rootStem: T.SingleOrLengthOpts<T.PsString>,
|
||||
} {
|
||||
const vInfo = getVerbInfo(verb.entry) as T.SimpleVerbInfo;
|
||||
const rs = vInfo[isPast ? "root" : "stem"];
|
||||
const noPerfective = isTlulVerb(verb) || isKedul(verb);
|
||||
const rs = vInfo[(isPast || isAbility || voice === "passive") ? "root" : "stem"];
|
||||
if (noPerfective && isAbility) {
|
||||
// exception with tlul verbs for ability stems
|
||||
return {
|
||||
perfectiveHead: undefined,
|
||||
rootStem: noPersInfs(rs.imperfective),
|
||||
};
|
||||
}
|
||||
if (aspect === "perfective" && rs.perfectiveSplit) {
|
||||
return extractPerfectiveSplit(rs.perfectiveSplit);
|
||||
}
|
||||
return {
|
||||
perfectiveHead: undefined,
|
||||
rootStem: rs[aspect],
|
||||
// because the persInfs only happen with stative compound verbs,j
|
||||
// which we are handling differently now
|
||||
rootStem: noPersInfs(rs[aspect]),
|
||||
}
|
||||
|
||||
function extractPerfectiveSplit(splitInfo: T.SplitInfo): ReturnType<typeof getRootStem> {
|
||||
// TODO: allow for infs
|
||||
const si = noPersInfs(splitInfo);
|
||||
// this is just for tlul and Daredul ?
|
||||
const si = fromPersInfls(splitInfo, person);
|
||||
if ("long" in si) {
|
||||
return {
|
||||
perfectiveHead: si.long[0],
|
||||
|
@ -132,6 +327,42 @@ function getRootStem(verb: T.VerbEntry, aspect: T.Aspect, isPast: boolean): {
|
|||
}
|
||||
}
|
||||
|
||||
function addEnding({ rootStem, ending, person, isPast, verb, aspect }:{
|
||||
rootStem: T.SingleOrLengthOpts<T.PsString>,
|
||||
ending: T.SingleOrLengthOpts<T.PsString[]>,
|
||||
person: T.Person,
|
||||
isPast: boolean,
|
||||
verb: T.VerbEntry,
|
||||
aspect: T.Aspect,
|
||||
}): T.SingleOrLengthOpts<T.PsString[]> {
|
||||
// TODO: no need for useless abbreviation now
|
||||
const rs = rootStem;
|
||||
const end = ending;
|
||||
const idiosyncratic3rdPast = isPast && person === T.Person.ThirdSingMale;
|
||||
const safeEndAdd = (rs: T.PsString) => (ending: T.PsString): T.PsString => (
|
||||
(ending.p === "ل" && rs.p.slice(-1) === "ل")
|
||||
? rs
|
||||
: concatPsString(rs, ending)
|
||||
);
|
||||
if ("long" in rs) {
|
||||
const endLong = getLength(end, "long");
|
||||
const endShort = getLength(end, "short");
|
||||
const shortForm = idiosyncratic3rdPast
|
||||
? ensure3rdPast(endShort, rs.short, verb)
|
||||
: endShort.map(e => concatPsString(rs.short, e));
|
||||
return {
|
||||
long: endLong.map(safeEndAdd(rs.long)),
|
||||
short: aspect === "imperfective"
|
||||
? applyImperfectiveShortAccent(shortForm, yulEndingInfinitive(removeFVarients(verb.entry)))
|
||||
: shortForm,
|
||||
};
|
||||
}
|
||||
if ("long" in end) {
|
||||
throw new Error("should not be using verb stems with long and short endings");
|
||||
}
|
||||
return end.map((e) => concatPsString(rs, e));
|
||||
}
|
||||
|
||||
function getEnding(person: T.Person, isPast: boolean) {
|
||||
const [row, col] = getVerbBlockPosFromPerson(person);
|
||||
return isPast ? {
|
||||
|
@ -140,3 +371,147 @@ function getEnding(person: T.Person, isPast: boolean) {
|
|||
} : presentEndings[row][col];
|
||||
}
|
||||
|
||||
function perfectTenseToEquative(t: T.PerfectTense): keyof typeof equativeEndings {
|
||||
return t === "presentPerfect"
|
||||
? "present"
|
||||
: t === "futurePerfect"
|
||||
? "habitual"
|
||||
: t === "habitualPerfect"
|
||||
? "habitual"
|
||||
: t === "pastPerfect"
|
||||
? "past"
|
||||
: t === "pastSubjunctivePerfect"
|
||||
? "pastSubjunctive"
|
||||
: t === "subjunctivePerfect"
|
||||
? "subjunctive"
|
||||
: t === "wouldBePerfect"
|
||||
? "past"
|
||||
: "subjunctive"
|
||||
}
|
||||
|
||||
function chooseParticipleInflection(pinf: T.SingleOrLengthOpts<T.UnisexInflections>, p: T.Person): T.SingleOrLengthOpts<T.PsString[]> {
|
||||
if ("long" in pinf) {
|
||||
return {
|
||||
short: chooseParticipleInflection(pinf.short, p) as T.PsString[],
|
||||
long: chooseParticipleInflection(pinf.long, p) as T.PsString[],
|
||||
};
|
||||
}
|
||||
if ("masc" in pinf) {
|
||||
const gender = personGender(p);
|
||||
const infNum = personIsPlural(p) ? 1 : 0;
|
||||
return pinf[gender][infNum];
|
||||
}
|
||||
return pinf; // already just one thing
|
||||
}
|
||||
|
||||
function addAbilityTailsToRs(rs: T.SingleOrLengthOpts<T.PsString>, aspect: T.Aspect, noPerfective: boolean): T.SingleOrLengthOpts<T.PsString[]> {
|
||||
if (!("long" in rs)) {
|
||||
throw new Error("rootStem for ability verb should have short and long versions");
|
||||
}
|
||||
const tails: T.PsString[] = [
|
||||
{ p: "ی", f: "ey" },
|
||||
{ p: "ای", f: "aay" },
|
||||
];
|
||||
const accentedTails: T.PsString[] = [
|
||||
{ p: "ی", f: "éy" },
|
||||
{ p: "ای", f: "áay" },
|
||||
];
|
||||
// for single syllable long verb stems like tlul - ensure the accent
|
||||
const psLong = (aspect === "perfective" && !noPerfective)
|
||||
? removeAccents(rs.long)
|
||||
: ensureAccentLongStem(rs.long);
|
||||
return {
|
||||
long: tails.map(t => concatPsString(psLong, t)),
|
||||
short: (aspect === "perfective" && !noPerfective ? tails : accentedTails)
|
||||
.map(t => concatPsString(rs.short, t)),
|
||||
};
|
||||
}
|
||||
|
||||
function applyImperfectiveShortAccent(form: T.PsString[], yulEnding: boolean): T.PsString[] {
|
||||
return form.map(f => {
|
||||
return accentOnNFromEnd(f, yulEnding ? 1 : 0);
|
||||
});
|
||||
}
|
||||
|
||||
function ensure3rdPast(ending: T.PsString[], rs: T.PsString, verb: T.VerbEntry): T.PsString[] {
|
||||
if (isKedul(verb) && rs.p === "شو") {
|
||||
return [{ p: "شو", f: "sho" }];
|
||||
}
|
||||
if (isKawulVerb(verb) && rs.p === "کړ") {
|
||||
return [
|
||||
{ p: "کړ", f: "kuR" },
|
||||
{ p: "کړه", f: "kRu" },
|
||||
{ p: "کړو", f: "kRo" },
|
||||
];
|
||||
}
|
||||
if (isTlulVerb(verb) && rs.p === "غل") {
|
||||
return [{ p: "غی", f: "ghey" }];
|
||||
}
|
||||
if (verb.entry.tppp && verb.entry.tppf) {
|
||||
const tip = makePsString(verb.entry.tppp, verb.entry.tppf)
|
||||
// if it ends in a consonant, the special form will also have another
|
||||
// variation ending with a ه - u
|
||||
const endsInAConsonant = (pashtoConsonants.includes(tip.p.slice(-1)) || tip.f.slice(-1) === "w");
|
||||
return [
|
||||
tip,
|
||||
...endsInAConsonant ? [
|
||||
concatPsString(tip, { p: "ه", f: "u" }),
|
||||
concatPsString(tip, { p: "و", f: "o" }),
|
||||
] : [],
|
||||
];
|
||||
}
|
||||
const endsInAwul = (
|
||||
(["awul", "awúl"].includes(removeFVarients(verb.entry.f).slice(-4)))
|
||||
&&
|
||||
(verb.entry.p.slice(-2) === "ول")
|
||||
);
|
||||
// TODO: check about verbs like tawul (if they exist)
|
||||
if (endsInAwul) {
|
||||
const base = { p: rs.p.slice(0, -1), f: rs.f.slice(0, -2) };
|
||||
return [concatPsString(base, { p: "اوه", f: "aawu" })];
|
||||
}
|
||||
|
||||
// nothing special or idiosyncratic needed for 3rd pers masc sing past
|
||||
return ending.map(e => concatPsString(rs, e));
|
||||
}
|
||||
|
||||
function getAspect(tense: T.VerbTense | T.ModalTense): T.Aspect {
|
||||
const t = tense.replace("Modal", "");
|
||||
if (["presentVerb", "imperfectiveFuture", "imperfectivePast", "habitualImperfectivePast"].includes(t)) {
|
||||
return "imperfective";
|
||||
} else {
|
||||
return "perfective";
|
||||
}
|
||||
}
|
||||
|
||||
function isKedul(v: T.VerbEntry): boolean {
|
||||
return v.entry.p === "کېدل";
|
||||
}
|
||||
|
||||
function fromPersInfls<U extends object>(x: T.OptionalPersonInflections<U>, person: T.Person): U {
|
||||
if ("mascSing" in x) {
|
||||
return x[getPersonInflectionsKey(person)];
|
||||
} else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
function ensureAccentLongStem(ps: T.PsString): T.PsString {
|
||||
if (ps.f.charAt(ps.f.length - 2) === "ú") {
|
||||
return ps;
|
||||
}
|
||||
return {
|
||||
p: ps.p,
|
||||
f: ps.f.slice(0, -2) + "úl",
|
||||
};
|
||||
}
|
||||
|
||||
function getKedulStatPerfect(person: T.Person, tense: T.PerfectTense): [PT, EQ] {
|
||||
const { verbBlocks: [pt, eq] } = renderVerb({
|
||||
verb: kedulStatVerb,
|
||||
tense,
|
||||
person,
|
||||
voice: "active",
|
||||
}) as { hasBa: true, verbBlocks: [PT, EQ] };
|
||||
return [pt, eq];
|
||||
}
|
|
@ -8,6 +8,11 @@ export function isTlulVerb(e: T.VerbEntry | T.VerbDictionaryEntry): boolean {
|
|||
return entry.f === "tlul" || entry.p === "راتلل" || entry.p === "درتلل" || entry.p === "ورتلل";
|
||||
}
|
||||
|
||||
export function isKawulVerb(e: T.VerbEntry | T.VerbDictionaryEntry): boolean {
|
||||
const entry = "entry" in e ? e.entry : e;
|
||||
return ["کول", "راکول", "درکول", "ورکول"].includes(entry.p);
|
||||
}
|
||||
|
||||
export function isNounEntry(e: T.Entry | T.DictionaryEntry): e is T.NounEntry {
|
||||
if ("entry" in e) return false;
|
||||
return !!(e.c && (e.c.includes("n. m.") || e.c.includes("n. f.")));
|
||||
|
|
|
@ -661,6 +661,8 @@ export type VerbSelectionComplete = Omit<VerbSelection, "object" | "verbTense" |
|
|||
tense: VerbFormName,
|
||||
}
|
||||
|
||||
export type Voice = "active" | "passive";
|
||||
|
||||
export type VerbSelection = {
|
||||
type: "verb",
|
||||
verb: VerbEntry,
|
||||
|
@ -669,7 +671,7 @@ export type VerbSelection = {
|
|||
canChangeTransitivity: boolean,
|
||||
canChangeStatDyn: boolean,
|
||||
isCompound: "stative" | "dynamic" | false,
|
||||
voice: "active" | "passive",
|
||||
voice: Voice,
|
||||
canChangeVoice: boolean,
|
||||
negative: boolean,
|
||||
verbTense: VerbTense,
|
||||
|
|
Loading…
Reference in New Issue