diff --git a/src/lib/src/parsing/compare.ts b/src/lib/src/parsing/compare.ts index e69de29..6ce66e8 100644 --- a/src/lib/src/parsing/compare.ts +++ b/src/lib/src/parsing/compare.ts @@ -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", + }, +]; diff --git a/src/lib/src/parsing/parse-verb.test.ts b/src/lib/src/parsing/parse-verb.test.ts index f3d6014..eb13146 100644 --- a/src/lib/src/parsing/parse-verb.test.ts +++ b/src/lib/src/parsing/parse-verb.test.ts @@ -33,6 +33,7 @@ const alwatul = wordQuery("الوتل", "verb"); // TODO: azmoyul etc // TODO: cleaner and more thorough handling of ا seperating verbs ee - wee etc +// TODO: test imperatives const tests: { label: string; @@ -47,6 +48,10 @@ const tests: { persons: T.Person[]; aspects: T.Aspect[]; }; + imperative?: { + persons: T.Person[]; + aspects: T.Aspect[]; + }; verb: T.VerbEntry; }[]; }[]; @@ -191,11 +196,30 @@ const tests: { persons: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale], 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, }, ], }, - { input: "منلم", output: [ @@ -258,6 +282,10 @@ const tests: { persons: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale], aspects: ["imperfective", "perfective"], }, + imperative: { + persons: getPeople(2, "pl"), + aspects: ["imperfective", "perfective"], + }, verb: rasedul, }, ], @@ -309,7 +337,6 @@ const tests: { }, ], }, - { input: "خورې", output: [ @@ -322,7 +349,6 @@ const tests: { }, ], }, - { input: "خوړي", output: [], @@ -351,7 +377,6 @@ const tests: { }, ], }, - { input: "کوت", output: [ @@ -376,7 +401,6 @@ const tests: { }, ], }, - { input: "خلم", output: [ @@ -401,7 +425,6 @@ const tests: { }, ], }, - { input: "خیستلم", output: [ @@ -426,7 +449,6 @@ const tests: { }, ], }, - { input: "لوځې", output: [ @@ -913,7 +935,6 @@ const tests: { }, ], }, - // 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 // become raatlul etc... @@ -994,7 +1015,7 @@ tests.forEach(({ label, cases }) => { const madeVbsS = output.reduce((acc, o) => { return [ ...acc, - ...(["root", "stem"] as const).flatMap((base) => + ...(["root", "stem", "imperative"] as const).flatMap((base) => (o[base]?.aspects || []).flatMap((aspect) => (o[base]?.persons || []).flatMap((person) => [ { @@ -1003,8 +1024,13 @@ tests.forEach(({ label, cases }) => { info: { type: "verb" as const, aspect, - base, + base: base === "imperative" ? "stem" : base, verb: o.verb, + ...(base === "imperative" + ? { + imperative: true, + } + : {}), }, }, ]) diff --git a/src/lib/src/parsing/parse-verb.ts b/src/lib/src/parsing/parse-verb.ts index a00c5a3..2ab9ae8 100644 --- a/src/lib/src/parsing/parse-verb.ts +++ b/src/lib/src/parsing/parse-verb.ts @@ -36,7 +36,9 @@ export function parseVerb( 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) // TODO: can optimize this to not have to look for possible stems/roots if none const verbs = lookup(first.s, "verb"); @@ -44,7 +46,7 @@ export function parseVerb( // console.log({ verbs: JSON.stringify(verbs) }); // } // 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, body, errors: [], @@ -57,7 +59,8 @@ function matchVerbs( people: { root: T.Person[]; stem: T.Person[]; - } + }, + imperativePeople: T.Person[] ): T.ParsedVBE[] { const w: T.ParsedVBE[] = []; const lEnding = s.endsWith("ل"); @@ -65,7 +68,7 @@ function matchVerbs( const matchShortOrLong = (b: string, x: string) => { return b === x || (!lEnding && b === x.slice(0, -1)); }; - if (people.stem.length) { + if (people.stem.length || imperativePeople.length) { const stemMatches = { imperfective: entries.filter(({ entry: e }) => { 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; } -function getVerbEnding(p: string): { - root: T.Person[]; +function getImperativeVerbEnding(e: string): 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[]; + root: T.Person[]; } { - if (p.endsWith("م")) { + if (e === "م") { return { root: [T.Person.FirstSingMale, T.Person.FirstSingFemale], stem: [T.Person.FirstSingMale, T.Person.FirstSingFemale], }; - } else if (p.endsWith("ې")) { + } else if (e === "ې") { return { root: [ T.Person.SecondSingMale, @@ -309,7 +335,7 @@ function getVerbEnding(p: string): { ], stem: [T.Person.SecondSingMale, T.Person.SecondSingFemale], }; - } else if (p.endsWith("ي")) { + } else if (e === "ي") { return { stem: [ T.Person.ThirdSingMale, @@ -319,22 +345,22 @@ function getVerbEnding(p: string): { ], root: [], }; - } else if (p.endsWith("و")) { + } else if (e === "و") { return { root: [T.Person.FirstPlurMale, T.Person.FirstPlurFemale], stem: [T.Person.FirstPlurMale, T.Person.FirstPlurFemale], }; - } else if (p.endsWith("ئ")) { + } else if (e === "ئ") { return { root: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale], stem: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale], }; - } else if (p.endsWith("ه")) { + } else if (e === "ه") { return { root: [T.Person.ThirdSingFemale], stem: [], }; - } else if (p.endsWith("ل")) { + } else if (e === "ل") { return { root: [T.Person.ThirdPlurMale], stem: [], diff --git a/src/lib/src/parsing/parse-vp.ts b/src/lib/src/parsing/parse-vp.ts index 81df060..416f710 100644 --- a/src/lib/src/parsing/parse-vp.ts +++ b/src/lib/src/parsing/parse-vp.ts @@ -10,6 +10,7 @@ import { startsVerbSection, } from "./utils"; import { + getSubjectSelection, makeObjectSelectionComplete, makeSubjectSelectionComplete, } from "../phrase-building/blocks-utils"; @@ -22,8 +23,10 @@ import { parseBlocks } from "./parse-blocks"; import { makePronounSelection } from "../phrase-building/make-selections"; import { isFirstOrSecondPersPronoun } from "../phrase-building/render-vp"; import { LookupFunction } from "./lookup"; -import { personToGenNum } from "../misc-helpers"; +import { isSecondPerson, personToGenNum } from "../misc-helpers"; import { equals, zip } from "rambda"; +import { isPrimitive } from "util"; +import { isImperativeTense } from "../type-predicates"; // to hide equatives type-doubling issue // TODO: word query for kawul/kedul/stat/dyn @@ -74,16 +77,16 @@ function getTenses( blocks: T.ParsedBlock[], ba: boolean ): { - tense: T.VerbTense | T.PerfectTense; + tense: T.VerbTense | T.PerfectTense | T.ImperativeTense; person: T.Person; transitivities: T.Transitivity[]; negative: boolean; verb: T.VerbEntry; }[] { - const negIndex = blocks.findIndex( - (x) => x.type === "negative" && !x.imperative - ); - const negative = negIndex !== -1; + const negIndex = blocks.findIndex((x) => x.type === "negative"); + const negative: T.NegativeBlock | undefined = blocks[negIndex] as + | T.NegativeBlock + | undefined; const phIndex = blocks.findIndex((x) => x.type === "PH"); const vbeIndex = blocks.findIndex((x) => x.type === "VB"); const ph = phIndex !== -1 ? (blocks[phIndex] as T.ParsedPH) : undefined; @@ -101,13 +104,28 @@ function getTenses( 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); + if (verb.info.imperative && negative && !negative.imperative) { + return []; + } + if (!verb.info.imperative && negative && negative.imperative) { + return []; + } return [ { tense, transitivities, - negative, + negative: !!negative, person: verb.person, verb: verb.info.verb, }, @@ -141,7 +159,7 @@ function getTenses( { tense, transitivities, - negative, + negative: !!negative, person: equative.person, verb: pPart.info.verb, }, @@ -159,7 +177,7 @@ function finishPossibleVPSs({ tokens, person, }: { - tense: T.VerbTense | T.PerfectTense; + tense: T.VerbTense | T.PerfectTense | T.ImperativeTense; transitivities: T.Transitivity[]; npsAndAps: (T.ParsedNP | T.APSelection)[]; miniPronouns: T.ParsedMiniPronoun[]; @@ -169,49 +187,63 @@ function finishPossibleVPSs({ person: T.Person; }): T.ParseResult[] { const isPast = isPastTense(tense); - return transitivities.flatMap>( - (transitivity): T.ParseResult[] => { - const v: T.VerbSelectionComplete = { - type: "verb", - verb, - transitivity, - canChangeTransitivity: false, - canChangeStatDyn: false, - negative, - tense, - canChangeVoice: true, - isCompound: false, - voice: "active", - }; - if (transitivity === "intransitive") { - return finishIntransitive({ - miniPronouns, - npsAndAps, - tokens, - v, - person, - }); - } else if (transitivity === "transitive") { - return finishTransitive({ - miniPronouns, - npsAndAps, - tokens, - v, - person, - isPast, - }); - } else { - return finishGrammaticallyTransitive({ - miniPronouns, - npsAndAps, - tokens, - v, - person, - isPast, - }); + return transitivities + .flatMap>( + (transitivity): T.ParseResult[] => { + const v: T.VerbSelectionComplete = { + type: "verb", + verb, + transitivity, + canChangeTransitivity: false, + canChangeStatDyn: false, + negative, + tense, + canChangeVoice: true, + isCompound: false, + voice: "active", + }; + if (transitivity === "intransitive") { + return finishIntransitive({ + miniPronouns, + npsAndAps, + tokens, + v, + person, + }); + } else if (transitivity === "transitive") { + return finishTransitive({ + miniPronouns, + npsAndAps, + tokens, + v, + person, + isPast, + }); + } else { + return finishGrammaticallyTransitive({ + miniPronouns, + npsAndAps, + tokens, + v, + person, + isPast, + }); + } } - } + ) + .filter(checkImperative2ndPers); +} + +function checkImperative2ndPers({ + body: vps, +}: T.ParseResult): boolean { + if (!isImperativeTense(vps.verb.tense)) { + return true; + } + const subjectPerson = getPersonFromNP( + getSubjectSelection(vps.blocks).selection ); + return isSecondPerson(subjectPerson); } function finishIntransitive({ @@ -271,8 +303,9 @@ function finishIntransitive({ }, ]; } - if (getPersonFromNP(nps[0].selection) !== person) { - errors.push({ message: "subject must agree with intransitive verb" }); + const subjectPerson = getPersonFromNP(nps[0].selection); + if (isImperativeTense(v.tense) && !isSecondPerson(subjectPerson)) { + return []; } if (nps[0].inflected) { errors.push({ @@ -864,8 +897,21 @@ function getPeopleFromMiniPronouns(kids: T.ParsedKid[]): T.Person[] { function getTenseFromRootsStems( hasBa: boolean, base: "root" | "stem", - aspect: T.Aspect -): T.VerbTense { + aspect: T.Aspect, + 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 (base === "root") { return aspect === "perfective" ? "perfectivePast" : "imperfectivePast"; diff --git a/src/lib/src/phrase-building/vp-tools.ts b/src/lib/src/phrase-building/vp-tools.ts index b65f16b..37e99f7 100644 --- a/src/lib/src/phrase-building/vp-tools.ts +++ b/src/lib/src/phrase-building/vp-tools.ts @@ -291,8 +291,8 @@ export function uncompleteVPSelection( : isPerfectTense(tense) ? "perfect" : isImperativeTense(tense) - ? "modal" - : "imperative"; + ? "imperative" + : "modal"; return { ...vps, verb: { diff --git a/src/types.ts b/src/types.ts index 9327976..b9ab6fa 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1257,6 +1257,7 @@ export type VBE = VB & { aspect: Aspect; base: "stem" | "root"; verb: VerbEntry; + imperative?: true; abilityAux?: boolean; }; };