more work and testing on new verb engine

This commit is contained in:
adueck 2023-04-01 15:28:49 +04:00
parent c9e3d13c43
commit 833f5acce6
6 changed files with 1909 additions and 124 deletions

View File

@ -57,7 +57,6 @@ export function psJSXMap(ps: T.PsJSX, target: "p" | "f", dealWithString: (ps: T.
}, },
}; };
} catch (e) { } catch (e) {
console.error(e);
throw new Error("error mapping out PsJSX - unbalanced trees"); throw new Error("error mapping out PsJSX - unbalanced trees");
} }
} }

View File

@ -47,10 +47,13 @@ const testVerbTenses: T.VerbTense[] = [
const testPerfectTenses: T.PerfectTense[] = [ const testPerfectTenses: T.PerfectTense[] = [
"presentPerfect", "presentPerfect",
"futurePerfect",
"habitualPerfect",
"pastPerfect", "pastPerfect",
"subjunctivePerfect", "subjunctivePerfect",
"wouldBePerfect", "wouldBePerfect",
"wouldHaveBeenPerfect", "wouldHaveBeenPerfect",
"pastSubjunctivePerfect",
]; ];
const testAbilityTenses: T.ModalTense[] = testVerbTenses.map<T.ModalTense>(t => `${t}Modal`); const testAbilityTenses: T.ModalTense[] = testVerbTenses.map<T.ModalTense>(t => `${t}Modal`);

View File

@ -84,7 +84,11 @@ export function accentFSylsOnNFromEnd(syls: string[] | string, n: number): strin
} }
export function accentOnNFromEnd(ps: T.PsString, n: number): T.PsString { export function accentOnNFromEnd(ps: T.PsString, n: number): T.PsString {
const fSyls = splitUpSyllables(removeAccents(ps.f)); const fNoAccents = removeAccents(ps.f);
const fSyls = splitUpSyllables(fNoAccents);
// TODO: enable this and fix the tests it breaks!!!
// don't add accent if only one syllable
// if (fSyls.length === 1) return makePsString(ps.p, fNoAccents);
return makePsString( return makePsString(
ps.p, ps.p,
accentFSylsOnNFromEnd(fSyls, n), accentFSylsOnNFromEnd(fSyls, n),

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,4 @@
import * as T from "../../types";
import { import {
functionOnOptLengths, functionOnOptLengths,
getPersonInflectionsKey, getPersonInflectionsKey,
@ -5,11 +6,11 @@ import {
noPersInfs, noPersInfs,
personGender, personGender,
personIsPlural, personIsPlural,
personNumber,
} from "./misc-helpers"; } from "./misc-helpers";
import { import {
yulEndingInfinitive, yulEndingInfinitive,
} from "./p-text-helpers"; } from "./p-text-helpers";
import * as T from "../../types";
import { import {
concatPsString, concatPsString,
getLength, getLength,
@ -34,63 +35,9 @@ 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,
}; };
// export type RenderedVerbB = VerbRenderedBlock
// | PerfectiveHeadBlock
// | ModalVerbBlock
// | ModalVerbKedulPart
// | PerfectEquativeBlock
// | PerfectParticipleBlock;
// export type PerfectiveHeadBlock = { type: "perfectiveHead", ps: PsString };
// export type VerbRenderedBlock = {
// type: "verb",
// block: Omit<VerbSelectionComplete, "object"> & {
// hasBa: boolean,
// ps: SingleOrLengthOpts<PsString[]>,
// person: Person,
// complementWelded: undefined | Rendered<ComplementSelection> | Rendered<UnselectedComplementSelection>,
// },
// };
// 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 VA = {
type: "verbBlockWithAgreement",
ps: T.SingleOrLengthOpts<T.PsString[]>,
person: T.Person,
};
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 // 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 }: { export function renderVerb({ verb, tense, person, voice }: {
verb: T.VerbEntry, verb: T.VerbEntry,
@ -99,8 +46,10 @@ export function renderVerb({ verb, tense, person, voice }: {
voice: T.Voice, voice: T.Voice,
}): { }): {
hasBa: boolean, hasBa: boolean,
verbBlocks: VB[], verbBlocks: T.VB[],
} { } {
// TODO: check for transitivity with passive voice ??
// WARNING: this only works with simple verbs // WARNING: this only works with simple verbs
const hasBa = tenseHasBa(tense); const hasBa = tenseHasBa(tense);
if (isPerfectTense(tense)) { if (isPerfectTense(tense)) {
@ -115,27 +64,17 @@ export function renderVerb({ verb, tense, person, voice }: {
const { perfectiveHead, rootStem } = getRootStem({ const { perfectiveHead, rootStem } = getRootStem({
verb, aspect, isPast, isAbility, person, voice verb, aspect, isPast, isAbility, person, voice
}); });
const perfectiveHeadBlock: PH | undefined = perfectiveHead ? { const perfectiveHeadBlock: T.PH | undefined = perfectiveHead ? {
type: "perfectiveHeadBlock", type: "PH",
// should only need this for tlul and Daaredul? // should only need this for tlul and Daaredul?
ps: fromPersInfls(perfectiveHead, person), ps: fromPersInfls(perfectiveHead, person),
} : undefined; } : 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); const ending = getEnding(person, isPast);
if (isAbility) { if (isAbility) {
const [vb, shPart] = getAbilityVerbBlocks({ verb, isPast, person, rootStem, aspect, voice }); const [vb, shPart] = getAbilityVerbBlocks({ verb, isPast, person, root: rootStem, aspect, voice });
return { return {
hasBa, hasBa,
verbBlocks: perfectiveHeadBlock verbBlocks: (perfectiveHeadBlock && voice === "active")
? [perfectiveHeadBlock, vb, shPart] ? [perfectiveHeadBlock, vb, shPart]
: [vb, shPart], : [vb, shPart],
}; };
@ -150,8 +89,8 @@ export function renderVerb({ verb, tense, person, voice }: {
], ],
}; };
} }
const verbBlock: VA = { const verbBlock: T.VA = {
type: "verbBlockWithAgreement", type: "VA",
ps: addEnding({ ps: addEnding({
rootStem, ending, person, isPast, verb, aspect, rootStem, ending, person, isPast, verb, aspect,
}), }),
@ -169,7 +108,8 @@ function getPassiveVerbBlocks({ root, tense, person }: {
root: T.SingleOrLengthOpts<T.PsString>, root: T.SingleOrLengthOpts<T.PsString>,
tense: T.VerbTense, tense: T.VerbTense,
person: T.Person, person: T.Person,
}): Welded { }): T.Welded {
/* istanbul ignore next */
if (!("long" in root)) { if (!("long" in root)) {
throw new Error("should have length versions in roots for passive"); throw new Error("should have length versions in roots for passive");
} }
@ -178,33 +118,55 @@ function getPassiveVerbBlocks({ root, tense, person }: {
tense, tense,
person, person,
voice: "active", voice: "active",
}) as { hasBa: boolean, verbBlocks: [VA] }; }) as { hasBa: boolean, verbBlocks: [T.VA] };
return weld( return weld(
{ {
type: "verbBlockWithoutAgreement", type: "VI",
ps: [root.long], ps: [root.long],
}, },
auxVerb, auxVerb,
); );
} }
function getAbilityVerbBlocks({ verb, isPast, person, rootStem, aspect, voice }: { function getAbilityVerbBlocks({ verb, isPast, person, root, aspect, voice }: {
verb: T.VerbEntry, verb: T.VerbEntry,
isPast: boolean, isPast: boolean,
person: T.Person, person: T.Person,
rootStem: T.SingleOrLengthOpts<T.PsString>, root: T.SingleOrLengthOpts<T.PsString>,
aspect: T.Aspect, aspect: T.Aspect,
voice: T.Voice, voice: T.Voice,
}): VB[] { }): T.VB[] {
/* istanbul ignore next */
if (!("long" in root)) {
throw new Error("should have length versions in roots for passive");
}
const noPerfective = isTlulVerb(verb) || isKedul(verb); const noPerfective = isTlulVerb(verb) || isKedul(verb);
const shBlock = getAbilityShPart(isPast, person); const shBlock = getAbilityShPart(isPast, person);
if (voice === "passive") {
return [
weld(
{
type: "VI",
ps: [root.long],
},
{
type: "VI",
ps: addAbilityTailsToRs({
long: { p: "کېدل", f: "kedúl" },
short: { p: "کېد", f: "ked" },
}, aspect, true),
},
),
shBlock,
];
}
// TODO: this is redundant, we did it in another part of the program? // TODO: this is redundant, we did it in another part of the program?
const verbBlock: VPlain = { const verbBlock: T.VI = {
type: "verbBlockWithoutAgreement", type: "VI",
ps: addAbilityTailsToRs(rootStem, aspect, noPerfective), ps: addAbilityTailsToRs(root, aspect, noPerfective),
}; };
return [verbBlock, shBlock]; return [verbBlock, shBlock];
function getAbilityShPart(isPast: boolean, person: T.Person): VA { function getAbilityShPart(isPast: boolean, person: T.Person): T.VA {
// TODO: optimized shortcut version of this // TODO: optimized shortcut version of this
const { verbBlocks: [shBlock] } = renderVerb({ const { verbBlocks: [shBlock] } = renderVerb({
verb: kedulStatVerb, verb: kedulStatVerb,
@ -213,10 +175,10 @@ function getAbilityVerbBlocks({ verb, isPast, person, rootStem, aspect, voice }:
voice: "active", voice: "active",
}) as { }) as {
hasBa: boolean, hasBa: boolean,
verbBlocks: [VA], verbBlocks: [T.VA],
}; };
return { return {
type: "verbBlockWithAgreement", type: "VA",
ps: shBlock.ps, ps: shBlock.ps,
person, person,
}; };
@ -228,25 +190,25 @@ function getPerfectBlocks({ verb, tense, person, voice }: {
tense: T.PerfectTense, tense: T.PerfectTense,
person: T.Person, person: T.Person,
voice: T.Voice, voice: T.Voice,
}): VB[] { }): T.VB[] {
const vInfo = getVerbInfo(verb.entry) as T.SimpleVerbInfo; const vInfo = getVerbInfo(verb.entry) as T.SimpleVerbInfo;
// TODO: put this in a seperate function? // TODO: put this in a seperate function?
if (voice === "passive") { if (voice === "passive") {
const [pt, eq] = getKedulStatPerfect(person, tense); const [pt, eq] = getKedulStatPerfect(person, tense);
const passiveRoot: VPlain = { const passiveRoot: T.VI = {
type: "verbBlockWithoutAgreement", type: "VI",
ps: [noPersInfs(vInfo.root.imperfective).long], ps: [noPersInfs(vInfo.root.imperfective).long],
}; };
const welded: Welded = weld(passiveRoot, pt); const welded: T.Welded = weld(passiveRoot, pt);
return [welded, eq]; return [welded, eq];
} }
const equative = equativeEndings[perfectTenseToEquative(tense)]; const equative = equativeEndings[perfectTenseToEquative(tense)];
const [row, col] = getVerbBlockPosFromPerson(person); const [row, col] = getVerbBlockPosFromPerson(person);
const equativeBlock: EQ = { const equativeBlock: T.EQ = {
type: "equativeBlock", type: "EQ",
person, person,
ps: "long" in equative ? { ps: "long" in equative ? {
long: equative.long[row][col], long: equative.long[row][col],
@ -254,20 +216,18 @@ function getPerfectBlocks({ verb, tense, person, voice }: {
} : equative[row][col], } : equative[row][col],
} }
const participleBlock: T.PT = {
type: "PT",
gender: personGender(person),
const participleBlock: PT = { number: personNumber(person),
type: "participleBlock",
inflection: getPersonInflectionsKey(person),
ps: chooseParticipleInflection(inflectYey(noPersInfs(vInfo.participle.past)), person) ps: chooseParticipleInflection(inflectYey(noPersInfs(vInfo.participle.past)), person)
} }
return [participleBlock, equativeBlock]; return [participleBlock, equativeBlock];
} }
function weld(left: VPlain, right: VA | PT | VPlain): Welded { function weld(left: T.VI, right: T.VA | T.PT | T.VI): T.Welded {
return { return {
type: "weldedBlock", type: "welded",
left: { left: {
...left, ...left,
ps: functionOnOptLengths(left.ps, removeAccents), ps: functionOnOptLengths(left.ps, removeAccents),
@ -348,15 +308,20 @@ function addEnding({ rootStem, ending, person, isPast, verb, aspect }:{
const endLong = getLength(end, "long"); const endLong = getLength(end, "long");
const endShort = getLength(end, "short"); const endShort = getLength(end, "short");
const shortForm = idiosyncratic3rdPast const shortForm = idiosyncratic3rdPast
? ensure3rdPast(endShort, rs.short, verb) ? ensure3rdPast(endShort, rs.short, verb, aspect)
: endShort.map(e => concatPsString(rs.short, e)); : endShort.map(e => concatPsString(rs.short, e));
// TODO: this is a bit hacky
const rsLong = rs.long.f === "tlul"
? { p: "تلل", f: "tlúl" }
: rs.long;
return { return {
long: endLong.map(safeEndAdd(rs.long)), long: endLong.map(safeEndAdd(rsLong)),
short: aspect === "imperfective" short: aspect === "imperfective"
? applyImperfectiveShortAccent(shortForm, yulEndingInfinitive(removeFVarients(verb.entry))) ? applyImperfectiveShortAccent(shortForm, yulEndingInfinitive(removeFVarients(verb.entry)))
: shortForm, : shortForm,
}; };
} }
/* istanbul ignore next */
if ("long" in end) { if ("long" in end) {
throw new Error("should not be using verb stems with long and short endings"); throw new Error("should not be using verb stems with long and short endings");
} }
@ -382,8 +347,6 @@ function perfectTenseToEquative(t: T.PerfectTense): keyof typeof equativeEndings
? "past" ? "past"
: t === "pastSubjunctivePerfect" : t === "pastSubjunctivePerfect"
? "pastSubjunctive" ? "pastSubjunctive"
: t === "subjunctivePerfect"
? "subjunctive"
: t === "wouldBePerfect" : t === "wouldBePerfect"
? "past" ? "past"
: "subjunctive" : "subjunctive"
@ -396,18 +359,12 @@ function chooseParticipleInflection(pinf: T.SingleOrLengthOpts<T.UnisexInflectio
long: chooseParticipleInflection(pinf.long, p) as T.PsString[], long: chooseParticipleInflection(pinf.long, p) as T.PsString[],
}; };
} }
if ("masc" in pinf) { const gender = personGender(p);
const gender = personGender(p); const infNum = personIsPlural(p) ? 1 : 0;
const infNum = personIsPlural(p) ? 1 : 0; return pinf[gender][infNum];
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[]> { function addAbilityTailsToRs(rs: T.LengthOptions<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[] = [ const tails: T.PsString[] = [
{ p: "ی", f: "ey" }, { p: "ی", f: "ey" },
{ p: "ای", f: "aay" }, { p: "ای", f: "aay" },
@ -428,12 +385,16 @@ function addAbilityTailsToRs(rs: T.SingleOrLengthOpts<T.PsString>, aspect: T.Asp
} }
function applyImperfectiveShortAccent(form: T.PsString[], yulEnding: boolean): T.PsString[] { function applyImperfectiveShortAccent(form: T.PsString[], yulEnding: boolean): T.PsString[] {
// TODO: remove this hack - get proper working thing for accentOnNFromEnd not adding accent on 1 syllable words
if (form[0].f === "tu") {
return form;
}
return form.map(f => { return form.map(f => {
return accentOnNFromEnd(f, yulEnding ? 1 : 0); return accentOnNFromEnd(f, yulEnding ? 1 : 0);
}); });
} }
function ensure3rdPast(ending: T.PsString[], rs: T.PsString, verb: T.VerbEntry): T.PsString[] { function ensure3rdPast(ending: T.PsString[], rs: T.PsString, verb: T.VerbEntry, aspect: T.Aspect): T.PsString[] {
if (isKedul(verb) && rs.p === "شو") { if (isKedul(verb) && rs.p === "شو") {
return [{ p: "شو", f: "sho" }]; return [{ p: "شو", f: "sho" }];
} }
@ -444,8 +405,18 @@ function ensure3rdPast(ending: T.PsString[], rs: T.PsString, verb: T.VerbEntry):
{ p: "کړو", f: "kRo" }, { p: "کړو", f: "kRo" },
]; ];
} }
if (isTlulVerb(verb) && rs.p === "غل") { if (isTlulVerb(verb)) {
return [{ p: "غی", f: "ghey" }]; if (aspect === "perfective") {
return [{ p: "غی", f: "ghey" }];
}
const specialTuShort: T.PsString = {
p: rs.p.slice(0, -1) + "ه",
f: rs.f.slice(0, -1) + (rs.f === "tl" ? "u" : "ú"),
};
return [
specialTuShort,
concatPsString(rs, { p: "و", f: "o" }),
];
} }
if (verb.entry.tppp && verb.entry.tppf) { if (verb.entry.tppp && verb.entry.tppf) {
const tip = makePsString(verb.entry.tppp, verb.entry.tppf) const tip = makePsString(verb.entry.tppp, verb.entry.tppf)
@ -470,9 +441,12 @@ function ensure3rdPast(ending: T.PsString[], rs: T.PsString, verb: T.VerbEntry):
const base = { p: rs.p.slice(0, -1), f: rs.f.slice(0, -2) }; const base = { p: rs.p.slice(0, -1), f: rs.f.slice(0, -2) };
return [concatPsString(base, { p: "اوه", f: "aawu" })]; return [concatPsString(base, { p: "اوه", f: "aawu" })];
} }
const endsInDental = ["د", "ت"].includes(rs.p.slice(-1));
// nothing special or idiosyncratic needed for 3rd pers masc sing past // short endings like ورسېد
return ending.map(e => concatPsString(rs, e)); return (endsInDental ? [
"",
...ending,
] : ending).map(e => concatPsString(rs, e));
} }
function getAspect(tense: T.VerbTense | T.ModalTense): T.Aspect { function getAspect(tense: T.VerbTense | T.ModalTense): T.Aspect {
@ -506,12 +480,12 @@ function ensureAccentLongStem(ps: T.PsString): T.PsString {
}; };
} }
function getKedulStatPerfect(person: T.Person, tense: T.PerfectTense): [PT, EQ] { function getKedulStatPerfect(person: T.Person, tense: T.PerfectTense): [T.PT, T.EQ] {
const { verbBlocks: [pt, eq] } = renderVerb({ const { verbBlocks: [pt, eq] } = renderVerb({
verb: kedulStatVerb, verb: kedulStatVerb,
tense, tense,
person, person,
voice: "active", voice: "active",
}) as { hasBa: true, verbBlocks: [PT, EQ] }; }) as { hasBa: true, verbBlocks: [T.PT, T.EQ] };
return [pt, eq]; return [pt, eq];
} }

View File

@ -1069,3 +1069,47 @@ export type MiniPronoun = {
np: NPSelection, np: NPSelection,
}; };
export type VB = PH | VA | VI | PT | EQ | Welded;
/** perfective head block */
export type PH = {
type: "PH",
ps: PsString,
};
/** verb block with person agreement */
export type VA = {
type: "VA",
ps: SingleOrLengthOpts<PsString[]>,
person: Person,
};
/** invariable verb block */
export type VI = {
type: "VI",
ps: SingleOrLengthOpts<PsString[]>,
};
/** participle block with inflection */
export type PT = {
type: "PT",
ps: SingleOrLengthOpts<PsString[]>,
gender: Gender,
number: NounNumber,
};
/** equative block */
export type EQ = {
type: "EQ",
ps: SingleOrLengthOpts<PsString[]>,
person: Person,
};
/** 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
right: VA | PT | VI,
};