more on imperatives

This commit is contained in:
adueck 2023-10-02 22:28:07 -04:00
parent 8c336e8a71
commit 02b6396686
6 changed files with 422 additions and 80 deletions

View File

@ -0,0 +1,243 @@
const expected = [
{
info: {
aspect: "imperfective",
base: "stem",
imperative: true,
type: "verb",
verb: {
entry: {
c: "v. trans.",
e: "to accept, to believe",
ec: "accepting",
f: "manul",
g: "manul",
p: "منل",
r: 4,
tppf: "maanu",
tppp: "مانه",
ts: 1527815085,
},
},
},
person: 2,
type: "VB",
},
{
info: {
aspect: "imperfective",
base: "stem",
imperative: true,
type: "verb",
verb: {
entry: {
c: "v. trans.",
e: "to accept, to believe",
ec: "accepting",
f: "manul",
g: "manul",
p: "منل",
r: 4,
tppf: "maanu",
tppp: "مانه",
ts: 1527815085,
},
},
},
person: 3,
type: "VB",
},
{
info: {
aspect: "perfective",
base: "stem",
imperative: true,
type: "verb",
verb: {
entry: {
c: "v. trans.",
e: "to accept, to believe",
ec: "accepting",
f: "manul",
g: "manul",
p: "منل",
r: 4,
tppf: "maanu",
tppp: "مانه",
ts: 1527815085,
},
},
},
person: 2,
type: "VB",
},
{
info: {
aspect: "perfective",
base: "stem",
imperative: true,
type: "verb",
verb: {
entry: {
c: "v. trans.",
e: "to accept, to believe",
ec: "accepting",
f: "manul",
g: "manul",
p: "منل",
r: 4,
tppf: "maanu",
tppp: "مانه",
ts: 1527815085,
},
},
},
person: 3,
type: "VB",
},
];
const received = [
{
info: {
aspect: "imperfective",
base: "stem",
imperative: true,
type: "verb",
verb: {
entry: {
c: "v. trans.",
e: "to accept, to believe",
ec: "accepting",
f: "manul",
g: "manul",
p: "منل",
r: 4,
tppf: "maanu",
tppp: "مانه",
ts: 1527815085,
},
},
},
person: 2,
type: "VB",
},
{
info: {
aspect: "imperfective",
base: "stem",
imperative: true,
type: "verb",
verb: {
entry: {
c: "v. trans.",
e: "to accept, to believe",
ec: "accepting",
f: "manul",
g: "manul",
p: "منل",
r: 4,
tppf: "maanu",
tppp: "مانه",
ts: 1527815085,
},
},
},
person: 3,
type: "VB",
},
{
info: {
aspect: "perfective",
base: "stem",
imperative: true,
type: "verb",
verb: {
entry: {
c: "v. trans.",
e: "to accept, to believe",
ec: "accepting",
f: "manul",
g: "manul",
p: "منل",
r: 4,
tppf: "maanu",
tppp: "مانه",
ts: 1527815085,
},
},
},
person: 2,
type: "VB",
},
{
info: {
aspect: "perfective",
base: "stem",
imperative: true,
type: "verb",
verb: {
entry: {
c: "v. trans.",
e: "to accept, to believe",
ec: "accepting",
f: "manul",
g: "manul",
p: "منل",
r: 4,
tppf: "maanu",
tppp: "مانه",
ts: 1527815085,
},
},
},
person: 3,
type: "VB",
},
{
info: {
aspect: "imperfective",
base: "root",
type: "verb",
verb: {
entry: {
c: "v. trans.",
e: "to accept, to believe",
ec: "accepting",
f: "manul",
g: "manul",
p: "منل",
r: 4,
tppf: "maanu",
tppp: "مانه",
ts: 1527815085,
},
},
},
person: 5,
type: "VB",
},
{
info: {
aspect: "perfective",
base: "root",
type: "verb",
verb: {
entry: {
c: "v. trans.",
e: "to accept, to believe",
ec: "accepting",
f: "manul",
g: "manul",
p: "منل",
r: 4,
tppf: "maanu",
tppp: "مانه",
ts: 1527815085,
},
},
},
person: 5,
type: "VB",
},
];

View File

@ -33,6 +33,7 @@ const alwatul = wordQuery("الوتل", "verb");
// TODO: azmoyul etc // TODO: azmoyul etc
// TODO: cleaner and more thorough handling of ا seperating verbs ee - wee etc // TODO: cleaner and more thorough handling of ا seperating verbs ee - wee etc
// TODO: test imperatives
const tests: { const tests: {
label: string; label: string;
@ -47,6 +48,10 @@ const tests: {
persons: T.Person[]; persons: T.Person[];
aspects: T.Aspect[]; aspects: T.Aspect[];
}; };
imperative?: {
persons: T.Person[];
aspects: T.Aspect[];
};
verb: T.VerbEntry; verb: T.VerbEntry;
}[]; }[];
}[]; }[];
@ -191,11 +196,30 @@ const tests: {
persons: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale], persons: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale],
aspects: ["imperfective", "perfective"], aspects: ["imperfective", "perfective"],
}, },
imperative: {
persons: getPeople(2, "pl"),
aspects: ["imperfective", "perfective"],
},
verb: manul,
},
],
},
{
input: "منه",
output: [
{
imperative: {
persons: getPeople(2, "sing"),
aspects: ["imperfective", "perfective"],
},
root: {
persons: [T.Person.ThirdSingFemale],
aspects: ["imperfective", "perfective"],
},
verb: manul, verb: manul,
}, },
], ],
}, },
{ {
input: "منلم", input: "منلم",
output: [ output: [
@ -258,6 +282,10 @@ const tests: {
persons: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale], persons: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale],
aspects: ["imperfective", "perfective"], aspects: ["imperfective", "perfective"],
}, },
imperative: {
persons: getPeople(2, "pl"),
aspects: ["imperfective", "perfective"],
},
verb: rasedul, verb: rasedul,
}, },
], ],
@ -309,7 +337,6 @@ const tests: {
}, },
], ],
}, },
{ {
input: "خورې", input: "خورې",
output: [ output: [
@ -322,7 +349,6 @@ const tests: {
}, },
], ],
}, },
{ {
input: "خوړي", input: "خوړي",
output: [], output: [],
@ -351,7 +377,6 @@ const tests: {
}, },
], ],
}, },
{ {
input: "کوت", input: "کوت",
output: [ output: [
@ -376,7 +401,6 @@ const tests: {
}, },
], ],
}, },
{ {
input: "خلم", input: "خلم",
output: [ output: [
@ -401,7 +425,6 @@ const tests: {
}, },
], ],
}, },
{ {
input: "خیستلم", input: "خیستلم",
output: [ output: [
@ -426,7 +449,6 @@ const tests: {
}, },
], ],
}, },
{ {
input: "لوځې", input: "لوځې",
output: [ output: [
@ -913,7 +935,6 @@ const tests: {
}, },
], ],
}, },
// TODO: It would probably be more effecient just to return the kedul verb options // TODO: It would probably be more effecient just to return the kedul verb options
// and then when we put things together with the perfective head parsed they could // and then when we put things together with the perfective head parsed they could
// become raatlul etc... // become raatlul etc...
@ -994,7 +1015,7 @@ tests.forEach(({ label, cases }) => {
const madeVbsS = output.reduce<T.ParsedVBE[]>((acc, o) => { const madeVbsS = output.reduce<T.ParsedVBE[]>((acc, o) => {
return [ return [
...acc, ...acc,
...(["root", "stem"] as const).flatMap((base) => ...(["root", "stem", "imperative"] as const).flatMap((base) =>
(o[base]?.aspects || []).flatMap((aspect) => (o[base]?.aspects || []).flatMap((aspect) =>
(o[base]?.persons || []).flatMap<T.ParsedVBE>((person) => [ (o[base]?.persons || []).flatMap<T.ParsedVBE>((person) => [
{ {
@ -1003,8 +1024,13 @@ tests.forEach(({ label, cases }) => {
info: { info: {
type: "verb" as const, type: "verb" as const,
aspect, aspect,
base, base: base === "imperative" ? "stem" : base,
verb: o.verb, verb: o.verb,
...(base === "imperative"
? {
imperative: true,
}
: {}),
}, },
}, },
]) ])

View File

@ -36,7 +36,9 @@ export function parseVerb(
errors: [], errors: [],
})); }));
} }
const people = getVerbEnding(first.s); const ending = first.s.at(-1) || "";
const people = getVerbEnding(ending);
const imperativePeople = getImperativeVerbEnding(ending);
// First do rough verb lookup, grab wide pool of possible verbs (low searching complexity for fast lookup) // First do rough verb lookup, grab wide pool of possible verbs (low searching complexity for fast lookup)
// TODO: can optimize this to not have to look for possible stems/roots if none // TODO: can optimize this to not have to look for possible stems/roots if none
const verbs = lookup(first.s, "verb"); const verbs = lookup(first.s, "verb");
@ -44,7 +46,7 @@ export function parseVerb(
// console.log({ verbs: JSON.stringify(verbs) }); // console.log({ verbs: JSON.stringify(verbs) });
// } // }
// Then find out which ones match exactly and how // Then find out which ones match exactly and how
return matchVerbs(first.s, verbs, people).map((body) => ({ return matchVerbs(first.s, verbs, people, imperativePeople).map((body) => ({
tokens: rest, tokens: rest,
body, body,
errors: [], errors: [],
@ -57,7 +59,8 @@ function matchVerbs(
people: { people: {
root: T.Person[]; root: T.Person[];
stem: T.Person[]; stem: T.Person[];
} },
imperativePeople: T.Person[]
): T.ParsedVBE[] { ): T.ParsedVBE[] {
const w: T.ParsedVBE[] = []; const w: T.ParsedVBE[] = [];
const lEnding = s.endsWith("ل"); const lEnding = s.endsWith("ل");
@ -65,7 +68,7 @@ function matchVerbs(
const matchShortOrLong = (b: string, x: string) => { const matchShortOrLong = (b: string, x: string) => {
return b === x || (!lEnding && b === x.slice(0, -1)); return b === x || (!lEnding && b === x.slice(0, -1));
}; };
if (people.stem.length) { if (people.stem.length || imperativePeople.length) {
const stemMatches = { const stemMatches = {
imperfective: entries.filter(({ entry: e }) => { imperfective: entries.filter(({ entry: e }) => {
if (e.c.includes("comp")) { if (e.c.includes("comp")) {
@ -152,6 +155,19 @@ function matchVerbs(
}, },
}); });
}); });
imperativePeople.forEach((person) => {
w.push({
type: "VB",
person,
info: {
type: "verb",
aspect: aspect as T.Aspect,
base: "stem",
verb: removeFVarientsFromVerb(verb),
imperative: true,
},
});
});
}); });
}); });
} }
@ -291,16 +307,26 @@ function matchVerbs(
return w; return w;
} }
function getVerbEnding(p: string): { function getImperativeVerbEnding(e: string): T.Person[] {
root: T.Person[]; if (e === "ه") {
return [T.Person.SecondSingMale, T.Person.SecondSingFemale];
}
if (e === "ئ") {
return [T.Person.SecondPlurMale, T.Person.SecondPlurFemale];
}
return [];
}
function getVerbEnding(e: string): {
stem: T.Person[]; stem: T.Person[];
root: T.Person[];
} { } {
if (p.endsWith("م")) { if (e === "م") {
return { return {
root: [T.Person.FirstSingMale, T.Person.FirstSingFemale], root: [T.Person.FirstSingMale, T.Person.FirstSingFemale],
stem: [T.Person.FirstSingMale, T.Person.FirstSingFemale], stem: [T.Person.FirstSingMale, T.Person.FirstSingFemale],
}; };
} else if (p.endsWith("ې")) { } else if (e === "ې") {
return { return {
root: [ root: [
T.Person.SecondSingMale, T.Person.SecondSingMale,
@ -309,7 +335,7 @@ function getVerbEnding(p: string): {
], ],
stem: [T.Person.SecondSingMale, T.Person.SecondSingFemale], stem: [T.Person.SecondSingMale, T.Person.SecondSingFemale],
}; };
} else if (p.endsWith("ي")) { } else if (e === "ي") {
return { return {
stem: [ stem: [
T.Person.ThirdSingMale, T.Person.ThirdSingMale,
@ -319,22 +345,22 @@ function getVerbEnding(p: string): {
], ],
root: [], root: [],
}; };
} else if (p.endsWith("و")) { } else if (e === "و") {
return { return {
root: [T.Person.FirstPlurMale, T.Person.FirstPlurFemale], root: [T.Person.FirstPlurMale, T.Person.FirstPlurFemale],
stem: [T.Person.FirstPlurMale, T.Person.FirstPlurFemale], stem: [T.Person.FirstPlurMale, T.Person.FirstPlurFemale],
}; };
} else if (p.endsWith("ئ")) { } else if (e === "ئ") {
return { return {
root: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale], root: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale],
stem: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale], stem: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale],
}; };
} else if (p.endsWith("ه")) { } else if (e === "ه") {
return { return {
root: [T.Person.ThirdSingFemale], root: [T.Person.ThirdSingFemale],
stem: [], stem: [],
}; };
} else if (p.endsWith("ل")) { } else if (e === "ل") {
return { return {
root: [T.Person.ThirdPlurMale], root: [T.Person.ThirdPlurMale],
stem: [], stem: [],

View File

@ -10,6 +10,7 @@ import {
startsVerbSection, startsVerbSection,
} from "./utils"; } from "./utils";
import { import {
getSubjectSelection,
makeObjectSelectionComplete, makeObjectSelectionComplete,
makeSubjectSelectionComplete, makeSubjectSelectionComplete,
} from "../phrase-building/blocks-utils"; } from "../phrase-building/blocks-utils";
@ -22,8 +23,10 @@ import { parseBlocks } from "./parse-blocks";
import { makePronounSelection } from "../phrase-building/make-selections"; import { makePronounSelection } from "../phrase-building/make-selections";
import { isFirstOrSecondPersPronoun } from "../phrase-building/render-vp"; import { isFirstOrSecondPersPronoun } from "../phrase-building/render-vp";
import { LookupFunction } from "./lookup"; import { LookupFunction } from "./lookup";
import { personToGenNum } from "../misc-helpers"; import { isSecondPerson, personToGenNum } from "../misc-helpers";
import { equals, zip } from "rambda"; import { equals, zip } from "rambda";
import { isPrimitive } from "util";
import { isImperativeTense } from "../type-predicates";
// to hide equatives type-doubling issue // to hide equatives type-doubling issue
// TODO: word query for kawul/kedul/stat/dyn // TODO: word query for kawul/kedul/stat/dyn
@ -74,16 +77,16 @@ function getTenses(
blocks: T.ParsedBlock[], blocks: T.ParsedBlock[],
ba: boolean ba: boolean
): { ): {
tense: T.VerbTense | T.PerfectTense; tense: T.VerbTense | T.PerfectTense | T.ImperativeTense;
person: T.Person; person: T.Person;
transitivities: T.Transitivity[]; transitivities: T.Transitivity[];
negative: boolean; negative: boolean;
verb: T.VerbEntry; verb: T.VerbEntry;
}[] { }[] {
const negIndex = blocks.findIndex( const negIndex = blocks.findIndex((x) => x.type === "negative");
(x) => x.type === "negative" && !x.imperative const negative: T.NegativeBlock | undefined = blocks[negIndex] as
); | T.NegativeBlock
const negative = negIndex !== -1; | undefined;
const phIndex = blocks.findIndex((x) => x.type === "PH"); const phIndex = blocks.findIndex((x) => x.type === "PH");
const vbeIndex = blocks.findIndex((x) => x.type === "VB"); const vbeIndex = blocks.findIndex((x) => x.type === "VB");
const ph = phIndex !== -1 ? (blocks[phIndex] as T.ParsedPH) : undefined; const ph = phIndex !== -1 ? (blocks[phIndex] as T.ParsedPH) : undefined;
@ -101,13 +104,28 @@ function getTenses(
return []; return [];
} }
} }
const tense = getTenseFromRootsStems(ba, verb.info.base, verb.info.aspect); const tense = getTenseFromRootsStems(
ba,
verb.info.base,
verb.info.aspect,
!!negative,
verb.info.imperative
);
if (!tense) {
return [];
}
const transitivities = getTransitivities(verb.info.verb); const transitivities = getTransitivities(verb.info.verb);
if (verb.info.imperative && negative && !negative.imperative) {
return [];
}
if (!verb.info.imperative && negative && negative.imperative) {
return [];
}
return [ return [
{ {
tense, tense,
transitivities, transitivities,
negative, negative: !!negative,
person: verb.person, person: verb.person,
verb: verb.info.verb, verb: verb.info.verb,
}, },
@ -141,7 +159,7 @@ function getTenses(
{ {
tense, tense,
transitivities, transitivities,
negative, negative: !!negative,
person: equative.person, person: equative.person,
verb: pPart.info.verb, verb: pPart.info.verb,
}, },
@ -159,7 +177,7 @@ function finishPossibleVPSs({
tokens, tokens,
person, person,
}: { }: {
tense: T.VerbTense | T.PerfectTense; tense: T.VerbTense | T.PerfectTense | T.ImperativeTense;
transitivities: T.Transitivity[]; transitivities: T.Transitivity[];
npsAndAps: (T.ParsedNP | T.APSelection)[]; npsAndAps: (T.ParsedNP | T.APSelection)[];
miniPronouns: T.ParsedMiniPronoun[]; miniPronouns: T.ParsedMiniPronoun[];
@ -169,49 +187,63 @@ function finishPossibleVPSs({
person: T.Person; person: T.Person;
}): T.ParseResult<T.VPSelectionComplete>[] { }): T.ParseResult<T.VPSelectionComplete>[] {
const isPast = isPastTense(tense); const isPast = isPastTense(tense);
return transitivities.flatMap<T.ParseResult<T.VPSelectionComplete>>( return transitivities
(transitivity): T.ParseResult<T.VPSelectionComplete>[] => { .flatMap<T.ParseResult<T.VPSelectionComplete>>(
const v: T.VerbSelectionComplete = { (transitivity): T.ParseResult<T.VPSelectionComplete>[] => {
type: "verb", const v: T.VerbSelectionComplete = {
verb, type: "verb",
transitivity, verb,
canChangeTransitivity: false, transitivity,
canChangeStatDyn: false, canChangeTransitivity: false,
negative, canChangeStatDyn: false,
tense, negative,
canChangeVoice: true, tense,
isCompound: false, canChangeVoice: true,
voice: "active", isCompound: false,
}; voice: "active",
if (transitivity === "intransitive") { };
return finishIntransitive({ if (transitivity === "intransitive") {
miniPronouns, return finishIntransitive({
npsAndAps, miniPronouns,
tokens, npsAndAps,
v, tokens,
person, v,
}); person,
} else if (transitivity === "transitive") { });
return finishTransitive({ } else if (transitivity === "transitive") {
miniPronouns, return finishTransitive({
npsAndAps, miniPronouns,
tokens, npsAndAps,
v, tokens,
person, v,
isPast, person,
}); isPast,
} else { });
return finishGrammaticallyTransitive({ } else {
miniPronouns, return finishGrammaticallyTransitive({
npsAndAps, miniPronouns,
tokens, npsAndAps,
v, tokens,
person, v,
isPast, person,
}); isPast,
});
}
} }
} )
.filter(checkImperative2ndPers);
}
function checkImperative2ndPers({
body: vps,
}: T.ParseResult<T.VPSelectionComplete>): boolean {
if (!isImperativeTense(vps.verb.tense)) {
return true;
}
const subjectPerson = getPersonFromNP(
getSubjectSelection(vps.blocks).selection
); );
return isSecondPerson(subjectPerson);
} }
function finishIntransitive({ function finishIntransitive({
@ -271,8 +303,9 @@ function finishIntransitive({
}, },
]; ];
} }
if (getPersonFromNP(nps[0].selection) !== person) { const subjectPerson = getPersonFromNP(nps[0].selection);
errors.push({ message: "subject must agree with intransitive verb" }); if (isImperativeTense(v.tense) && !isSecondPerson(subjectPerson)) {
return [];
} }
if (nps[0].inflected) { if (nps[0].inflected) {
errors.push({ errors.push({
@ -864,8 +897,21 @@ function getPeopleFromMiniPronouns(kids: T.ParsedKid[]): T.Person[] {
function getTenseFromRootsStems( function getTenseFromRootsStems(
hasBa: boolean, hasBa: boolean,
base: "root" | "stem", base: "root" | "stem",
aspect: T.Aspect aspect: T.Aspect,
): T.VerbTense { negative: boolean,
imperative?: true
): T.VerbTense | T.ImperativeTense | undefined {
if (imperative) {
if (base === "root") {
return undefined;
}
if (hasBa) {
return undefined;
}
return aspect === "imperfective" || negative
? "imperfectiveImperative"
: "perfectiveImperative";
}
if (!hasBa) { if (!hasBa) {
if (base === "root") { if (base === "root") {
return aspect === "perfective" ? "perfectivePast" : "imperfectivePast"; return aspect === "perfective" ? "perfectivePast" : "imperfectivePast";

View File

@ -291,8 +291,8 @@ export function uncompleteVPSelection(
: isPerfectTense(tense) : isPerfectTense(tense)
? "perfect" ? "perfect"
: isImperativeTense(tense) : isImperativeTense(tense)
? "modal" ? "imperative"
: "imperative"; : "modal";
return { return {
...vps, ...vps,
verb: { verb: {

View File

@ -1257,6 +1257,7 @@ export type VBE = VB & {
aspect: Aspect; aspect: Aspect;
base: "stem" | "root"; base: "stem" | "root";
verb: VerbEntry; verb: VerbEntry;
imperative?: true;
abilityAux?: boolean; abilityAux?: boolean;
}; };
}; };