From bce8bf8365d6af95a136743f67a4e065bb2f0529 Mon Sep 17 00:00:00 2001 From: adueck Date: Thu, 15 Jun 2023 20:10:41 +0400 Subject: [PATCH] more work on new verb renderer --- README.md | 39 +- full-phrase-process.svg | 1 + src/components/src/blocks/Block.tsx | 223 +++---- .../src/vp-explorer/VPExplorerQuiz.tsx | 22 +- src/demo-components/VPBuilderDemo.tsx | 12 - src/lib/src/accent-helpers.ts | 58 ++ src/lib/src/dyn-comp-aux-verbs.ts | 14 +- src/lib/src/misc-helpers.ts | 14 + .../make-verb-selection.test.ts | 233 ++++++++ .../new-verb-engine/make-verb-selection.ts | 107 ++++ .../src/new-verb-engine/render-verb.test.ts | 34 +- src/lib/src/new-verb-engine/render-verb.ts | 5 +- src/lib/src/new-verb-engine/rs-helpers.ts | 7 +- src/lib/src/phrase-building/blocks-utils.ts | 173 +----- src/lib/src/phrase-building/compile.ts | 109 ++-- .../src/phrase-building/new-render-verb.txt | 31 - src/lib/src/phrase-building/render-vp.ts | 547 ++---------------- src/lib/src/verb-info.test.ts | 64 +- src/types.ts | 78 +-- 19 files changed, 766 insertions(+), 1005 deletions(-) create mode 100644 full-phrase-process.svg create mode 100644 src/lib/src/new-verb-engine/make-verb-selection.test.ts create mode 100644 src/lib/src/new-verb-engine/make-verb-selection.ts delete mode 100644 src/lib/src/phrase-building/new-render-verb.txt diff --git a/README.md b/README.md index b50c6cf..2996a35 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,47 @@ A pashto inflection, verb conjugation, and phrase-generation engine, as well as #### [Pashto Inflector Website/Demo](https://pashto-inflector.lingdocs.com) -This is **published as two libraries**: +## How it works + +This library uses a 3-step process to generate gramattically correct Pashto phrases. This 3-step process corresponds directly to Chomsky's tripartrite structure for tranformational generative grammars. + +| | Pashto Inflector Function | Chomskian Grammar Level | +|-|--------------------------| ----------------------- | +|1.| Assemble the phrase tree | Phrase Structure Rules | +|2.| Inflect the words in tree | Morphophonemic Rules | +|3.| Arrange the inflected words in order | Transformational Rules | + +### 1. Assemble the phrase tree + +**Phrase Structure Rules** + +An abstract syntax tree is created with the various parts of a phrase represented as dictionary entries of the words as well as information about gender, number, and tense, etc. These are arranged in typed structures that allow for arranging NP and APs into verbal or equative phrases. + +### 2. Inflect all the words in the phrase tree + +**Morphophonemic Rules** + +The dictionary entries for the words are inflected and conjugated as necessary. + +### 3. Arrange the inflected words + +**Transformational Rules** + +The inflected words are arranged into possible phrases with various options for dropping or encliticizing pronouns, and placing the negation particle in the correct place(s). + +![Phrase building process](./full-phrase-process.svg) +* The full process of phrase building + +## Distributions + +This is published on [a private NPM proxy registry](https://npm.lingdocs.com) as two libraries: - @lingdocs/inflect - - `/src/components` + - `/src/lib` - The core inflection engine with grammatical information and tools for processing LingDocs dictionary entries and Pashto text. - Can be used with Node 16, as CommonJS - @lingdocs/ps-react - - `/src/lib` + - `/src/components` - @lingdocs/inflect plus react components for displaying Pashto text, phrase engine UI etc. - Only available as an ES6 Module diff --git a/full-phrase-process.svg b/full-phrase-process.svg new file mode 100644 index 0000000..26c3986 --- /dev/null +++ b/full-phrase-process.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/src/blocks/Block.tsx b/src/components/src/blocks/Block.tsx index 80fd387..5104a4b 100644 --- a/src/components/src/blocks/Block.tsx +++ b/src/components/src/blocks/Block.tsx @@ -38,31 +38,32 @@ function Block({ opts, block, king, script }: { if (block.block.type === "negative") { return } - if (block.block.type === "perfectiveHead") { - return - } - if (block.block.type === "verb") { - return ; - } - if (block.block.type === "objectSelection") { - const role = king === "object" ? "king" : king === "subject" ? "servant" : undefined; - return ; - } - if (block.block.type === "perfectParticipleBlock") { - return ; - } - if (block.block.type === "perfectEquativeBlock") { - return ; - } - if (block.block.type === "modalVerbBlock") { - return ; - } - if (block.block.type === "modalVerbKedulPart") { - return - } - if (block.block.type === "complement") { - return ; - } + return
TODO
+ // if (block.block.type === "perfectiveHead") { + // return + // } + // if (block.block.type === "verb") { + // return ; + // } + // if (block.block.type === "objectSelection") { + // const role = king === "object" ? "king" : king === "subject" ? "servant" : undefined; + // return ; + // } + // if (block.block.type === "perfectParticipleBlock") { + // return ; + // } + // if (block.block.type === "perfectEquativeBlock") { + // return ; + // } + // if (block.block.type === "modalVerbBlock") { + // return ; + // } + // if (block.block.type === "modalVerbKedulPart") { + // return + // } + // if (block.block.type === "complement") { + // return ; + // } return null; } @@ -80,96 +81,96 @@ function Border({ children, extraClassName, padding }: { children: JSX.Element | } -function VerbSBlock({ opts, v, script }: { - opts: T.TextOptions, - script: "p" | "f", - v: T.VerbRenderedBlock["block"] | T.PerfectParticipleBlock, -}) { - const [length, setLength] = useState("long"); - function changeLength() { - setLength(o => ( - o === "long" - ? "short" - : o === "short" && "mini" in v.ps - ? "mini" - : "long" - )); - } - return
- {"long" in v.ps &&
{length}
} - - <> - {(v.type === "verb" || v.type === "perfectParticipleBlock") && v.complementWelded && - - } - {getLength(v.ps, length)[0][script]} - - -
{v.type === "perfectParticipleBlock" ? "Past Partic." : "Verb"}
- {(v.type === "perfectParticipleBlock" - ? getEnglishParticipleInflection - : getEnglishPersonInfo - )(v.person, "short")} -
-} +// function VerbSBlock({ opts, v, script }: { +// opts: T.TextOptions, +// script: "p" | "f", +// v: T.VerbRenderedBlock["block"] | T.PerfectParticipleBlock, +// }) { +// const [length, setLength] = useState("long"); +// function changeLength() { +// setLength(o => ( +// o === "long" +// ? "short" +// : o === "short" && "mini" in v.ps +// ? "mini" +// : "long" +// )); +// } +// return
+// {"long" in v.ps &&
{length}
} +// +// <> +// {(v.type === "verb" || v.type === "perfectParticipleBlock") && v.complementWelded && +// +// } +// {getLength(v.ps, length)[0][script]} +// +// +//
{v.type === "perfectParticipleBlock" ? "Past Partic." : "Verb"}
+// {(v.type === "perfectParticipleBlock" +// ? getEnglishParticipleInflection +// : getEnglishPersonInfo +// )(v.person, "short")} +//
+// } -function ModalVerbBlock({ opts, v, script }: { - opts: T.TextOptions, - script: "p" | "f", - v: T.ModalVerbBlock, -}) { - const [length, setLength] = useState("long"); - function changeLength() { - setLength(o => ( - o === "long" - ? "short" - : "long" - )); - } - return
- {"long" in v.ps &&
{length}
} - - <> - {v.complementWelded && - - } - {getLength(v.ps, length)[0][script]} - - -
Verb
- Ability -
-} +// function ModalVerbBlock({ opts, v, script }: { +// opts: T.TextOptions, +// script: "p" | "f", +// v: T.ModalVerbBlock, +// }) { +// const [length, setLength] = useState("long"); +// function changeLength() { +// setLength(o => ( +// o === "long" +// ? "short" +// : "long" +// )); +// } +// return
+// {"long" in v.ps &&
{length}
} +// +// <> +// {v.complementWelded && +// +// } +// {getLength(v.ps, length)[0][script]} +// +// +//
Verb
+// Ability +//
+// } -function PerfHeadBlock({ opts, ps, script }: { - opts: T.TextOptions, - ps: T.PsString, - script: "p" | "f", +// function PerfHeadBlock({ opts, ps, script }: { +// opts: T.TextOptions, +// ps: T.PsString, +// script: "p" | "f", -}) { - return
- - {ps[script]} - -
perf. head
- {'\u00A0'} -
; -} +// }) { +// return
+// +// {ps[script]} +// +//
perf. head
+// {'\u00A0'} +//
; +// } -function ModalAuxBlock({ opts, aux, script }: { - opts: T.TextOptions, - aux: T.ModalVerbKedulPart, - script: "p" | "f", +// function ModalAuxBlock({ opts, aux, script }: { +// opts: T.TextOptions, +// aux: T.ModalVerbKedulPart, +// script: "p" | "f", -}) { - return
- - {aux.ps[0][script]} - -
Abil. Aux
- {getEnglishPersonInfo(aux.verb.block.person, "short")} -
; -} +// }) { +// return
+// +// {aux.ps[0][script]} +// +//
Abil. Aux
+// {getEnglishPersonInfo(aux.verb.block.person, "short")} +//
; +// } function NegBlock({ opts, imperative, script }: { opts: T.TextOptions, @@ -187,7 +188,7 @@ function NegBlock({ opts, imperative, script }: { function EquativeBlock({ opts, eq, script }: { opts: T.TextOptions, - eq: T.EquativeRendered | T.PerfectEquativeBlock, + eq: T.EquativeRendered, script: "p" | "f", }) { const [length, setLength] = useState("long"); diff --git a/src/components/src/vp-explorer/VPExplorerQuiz.tsx b/src/components/src/vp-explorer/VPExplorerQuiz.tsx index d60fd98..ac2b9b9 100644 --- a/src/components/src/vp-explorer/VPExplorerQuiz.tsx +++ b/src/components/src/vp-explorer/VPExplorerQuiz.tsx @@ -25,7 +25,6 @@ import { getObjectSelectionFromBlocks, getSubjectSelectionFromBlocks, getSubjectSelection, - getVerbAndHeadFromBlocks, } from "../../../lib/src/phrase-building/blocks-utils"; const correctEmoji = ["✅", '🤓', "✅", '😊', "🌹", "✅", "✅", '🥳', "👏", "✅", "💯", "😎", "✅", "👍"]; @@ -354,17 +353,18 @@ function tickQuizState(startingWith: T.VPSelectionComplete | QuizState): QuizSta } function getBlanksAnswer(vps: T.VPSelectionComplete): { ps: T.PsString[], withBa: boolean } { - const { verb, perfectiveHead } = getVerbAndHeadFromBlocks(renderVP(vps).blocks); - const ps = flattenLengths(verb.block.ps).map(x => { - const y = removeBa(x); - if (perfectiveHead) { - return concatPsString(perfectiveHead.ps, y); - } - return y; - }); + // TODO: !!! + // const { verb, perfectiveHead } = getVerbAndHeadFromBlocks(renderVP(vps).blocks); + // const ps = flattenLengths(verb.block.ps).map(x => { + // const y = removeBa(x); + // if (perfectiveHead) { + // return concatPsString(perfectiveHead.ps, y); + // } + // return y; + // }); return { - ps, - withBa: verb.block.hasBa, + ps: [{ p: "TOOD", f: "TODO" }], + withBa: false, // verb.block.hasBa, } } diff --git a/src/demo-components/VPBuilderDemo.tsx b/src/demo-components/VPBuilderDemo.tsx index 4cd6a99..e4fef3f 100644 --- a/src/demo-components/VPBuilderDemo.tsx +++ b/src/demo-components/VPBuilderDemo.tsx @@ -12,7 +12,6 @@ import { randomNumber, } from "../lib/src/misc-helpers"; import { entryFeeder } from "./entryFeeder"; -import { renderVerb } from "../lib/src/new-verb-engine/render-verb"; import NPPronounPicker from "../components/src/np-picker/NPPronounPicker"; const transitivities: T.Transitivity[] = [ @@ -143,14 +142,6 @@ 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; // const rs = v ? getAllRs(v.verb as T.VerbEntry) : undefined return
@@ -250,9 +241,6 @@ function VPBuilderDemo({ opts }: { role="subject" opts={opts} /> -
-            {JSON.stringify(rv, null, "  ")}
-        
{v?.verb.entry &&
s.includes(x.accented)); } + +export function removeVerbAccent([a, b]: T.VerbRenderedOutput): T.VerbRenderedOutput { + return [ + removeVHeadAccent(a), + removeVAccent(b), + ]; +} + +function removeVHeadAccent([v]: [T.VHead] | []): [T.VHead] | [] { + if (v === undefined) { + return []; + } + if (v.type === "PH") { + return [{ + ...v, + ps: removeAccents(v.ps), + }]; + } + return [{ + ...v, + comp: removeCompAccent(v.comp), + }]; +} + +function removeCompAccent(comp: T.Comp): T.Comp { + return { + ...comp, + ps: removeAccents(comp.ps), + } +} + +function removeVAccent(v: [T.VB, T.VBE] | [T.VBE]): [T.VB, T.VBE]| [T.VBE] { + return v.map(removeVBAccent) as [T.VB, T.VBE]| [T.VBE]; +} + +function removeVBAccent(v: V): V { + if (v.type === "welded") { + return { + ...v, + left: removeWeldedLeftAccent(v.left), + } + } + return { + ...v, + ps: applyToSingOrLengthOpts(removeAccents, v.ps), + }; +} + +function removeWeldedLeftAccent(v: T.NComp | T.VBBasic | T.Welded) { + if (v.type === "NComp") { + return { + ...v, + comp: removeCompAccent(v.comp), + }; + } + return removeVBAccent(v); +} diff --git a/src/lib/src/dyn-comp-aux-verbs.ts b/src/lib/src/dyn-comp-aux-verbs.ts index 250c827..d62d1e7 100644 --- a/src/lib/src/dyn-comp-aux-verbs.ts +++ b/src/lib/src/dyn-comp-aux-verbs.ts @@ -17,7 +17,7 @@ export const dynamicAuxVerbs: Array< } > = [ { - entry: {"i":10058,"ts":1527812752,g: "","p":"کول","f":"kawul","e":"to do (an action or activity)","c":"v. trans. irreg. dyn. aux.","ssp":"وکړ","ssf":"óokR","prp":"وکړل","prf":"óokRul","pprtp":"کړی","pprtf":"kúRey","diacExcept":true}, + entry: {"ts":1527812752,"i":11033,"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","separationAtP":1,"separationAtF":2,"diacExcept":true,"ec":"do,does,doing,did,done"}, }, { entry: {"i":10122,"ts":1527812754,g: "","p":"کېدل","f":"kedul","e":"to happen, occur","c":"v. intrans. irreg. aux. dyn.","ssp":"وش","ssf":"óosh","prp":"وشول","prf":"óoshwul","pprtp":"شوی","pprtf":"shúwey","diacExcept":true}, @@ -47,17 +47,7 @@ export const dynamicAuxVerbs: Array< complement: {"i":3774,"ts":1527813139,g: "","p":"تېر","f":"ter","e":"last, past, previous, passed, gone over","c":"adj."}, }, { - entry: { - ts: 1527815399, - p: "وهل", - f: "wahul", - g: "", - e: "to hit", - c: "v. trans.", - i: 12183, - tppp: "واهه", - tppf: "waahu", - }, + entry: {"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"}, }, { entry: { diff --git a/src/lib/src/misc-helpers.ts b/src/lib/src/misc-helpers.ts index c7ffac2..a71c52a 100644 --- a/src/lib/src/misc-helpers.ts +++ b/src/lib/src/misc-helpers.ts @@ -8,6 +8,20 @@ import * as T from "../../types"; +export function applyToSingOrLengthOpts(f: (x: X) => X, x: T.SingleOrLengthOpts): T.SingleOrLengthOpts { + if ("long" in x) { + return { + long: f(x.long), + short: f(x.short), + ..."mini" in x && x.mini ? { + mini: f(x.mini), + } : {}, + }; + } else { + return f(x); + } +} + export const blank: T.PsString = { p: "_____", f: "_____", diff --git a/src/lib/src/new-verb-engine/make-verb-selection.test.ts b/src/lib/src/new-verb-engine/make-verb-selection.test.ts new file mode 100644 index 0000000..aeb85fa --- /dev/null +++ b/src/lib/src/new-verb-engine/make-verb-selection.test.ts @@ -0,0 +1,233 @@ +import * as T from "../../../types"; +import { makeVerbSelection } from "./make-verb-selection"; +import { vEntry } from "./rs-helpers"; + +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 kawulDyn = vEntry({"ts":1527812752,"i":11033,"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","separationAtP":1,"separationAtF":2}); +const wahulNoC = vEntry({"ts":1527815399,"i":15049,"p":"وهل","f":"wahul","g":"wahul","e":"to hit","r":4,"tppp":"واهه","tppf":"waahu","ec":"hit,hits,hitting,hit,hit"}); +const wahulNoC2 = vEntry({"ts":1527815399,"i":15049,"p":"وهل","f":"wahul","g":"wahul",c:"v","e":"to hit","r":4,"tppp":"واهه","tppf":"waahu","ec":"hit,hits,hitting,hit,hit"}); +const khandul = vEntry({"ts":1527812767,"i":5896,"p":"خندل","f":"khandul","g":"khandul","e":"to laugh","r":4,"c":"v. gramm. trans.","psp":"خاند","psf":"khaand","ec":"laugh"}); +const leedul = vEntry({"ts":1527812275,"i":12053,"p":"لیدل","f":"leedul","g":"leedul","e":"to see","r":4,"c":"v. trans./gramm. trans.","psp":"وین","psf":"ween","tppp":"لید","tppf":"leed","ec":"see,sees,seeing,saw,seen"}); +const ghadzedul = vEntry({"ts":1527812615,"i":9500,"p":"غځېدل","f":"ghadzedul","g":"ghadzedul","e":"stretch out, lie, be extended, expand","r":3,"c":"v. intrans.","ec":"stretch","ep":"out"}); +const bandawul = vEntry( + {"ts":1527821309,"i":1792,"p":"بندول","f":"bandawul","g":"bandawul","e":"to close, block, stop, barricade, cut off, restrain, hold back","r":3,"c":"v. stat. comp. trans.","l":1577301753727,"ec":"close"}, + {"ts":1577301753727,"i":1780,"p":"بند","f":"band","g":"band","e":"closed, blocked, stopped","c":"adj."}, +); +const bandedul = vEntry( + {"ts":1588781671306,"i":1796,"p":"بندېدل","f":"bandedúl","g":"bandedul","e":"to be closed, blocked, stopped","r":4,"c":"v. stat. comp. intrans.","l":1577301753727,"ec":"be","ep":"closed"}, + {"ts":1577301753727,"i":1780,"p":"بند","f":"band","g":"band","e":"closed, blocked, stopped","c":"adj."}, +); +const mundaWahul = vEntry( + {"ts":1527812939,"i":13322,"p":"منډه وهل","f":"munDa wahul","g":"munDawahul","e":"to run","r":4,"c":"v. dyn. comp. trans.","l":1527815805,"ec":"run,runs,running,ran,run"}, + {"ts":1527815805,"i":13321,"p":"منډه","f":"múnDa","g":"munDa","e":"run, running","r":4,"c":"n. f."}, +); +const istreeKawul = vEntry( + {"ts":1658796089458,"i":519,"p":"استري کول","f":"istree kawul","g":"istreekawul","e":"to iron (clothes etc.)","r":4,"c":"v. dyn./stat. comp. trans.","l":1658795458148,"ec":"iron"}, + {"ts":1658795458148,"i":518,"p":"استري","f":"istree","g":"istree","e":"iron (for ironing clothes)","r":4,"c":"n. f."}, +); +const cheeghKawul = vEntry( + {"ts":1608137130992,"i":5190,"p":"چیغه کول","f":"chéegha kawul","g":"cheeghakawul","e":"to yell, scream, cry out","r":3,"c":"v. gen. stat./dyn. comp. trans.","l":1527813972}, + {"ts":1527813972,"i":5189,"p":"چیغه","f":"chéegha","g":"cheegha","e":"yell, scream, cry","r":3,"c":"n. f."}, +); +const kaarKawul = vEntry( + {"ts":1527812732,"i":10270,"p":"کار کول","f":"kaar kawul","g":"kaarkawul","e":"to work","r":4,"c":"v. dyn. comp. trans.","l":1527822084,"ec":"work"}, + {"ts":1527822084,"i":10268,"p":"کار","f":"kaar","g":"kaar","e":"work, job, business, stuff to do","r":4,"c":"n. m."}, +); + +const tests: { + verb: T.VerbEntry, + result: T.NewVerbSelection, +}[] = [ + { + verb: wahul, + result: { + type: "verb", + verb: wahul, + voice: "active", + canChangeVoice: true, + transitivity: "transitive", + compound: false, + canBeGenStat: false, + canChangeTransGenTrans: false, + variableRs: false, + canChangeStatDyn: false, + negative: false, + tense: "presentVerb", + }, + }, + { + verb: ghadzedul, + result: { + type: "verb", + verb: ghadzedul, + voice: "active", + canChangeVoice: false, + transitivity: "intransitive", + compound: false, + canBeGenStat: false, + canChangeTransGenTrans: false, + variableRs: false, + canChangeStatDyn: false, + negative: false, + tense: "presentVerb", + }, + }, + { + verb: khandul, + result: { + type: "verb", + verb: khandul, + voice: "active", + canChangeVoice: false, + transitivity: "grammatically transitive", + compound: false, + canBeGenStat: false, + canChangeTransGenTrans: false, + variableRs: false, + canChangeStatDyn: false, + negative: false, + tense: "presentVerb", + }, + }, + { + verb: leedul, + result: { + type: "verb", + verb: leedul, + voice: "active", + canChangeVoice: true, + transitivity: "transitive", + compound: false, + canBeGenStat: false, + canChangeTransGenTrans: true, + variableRs: false, + canChangeStatDyn: false, + negative: false, + tense: "presentVerb", + }, + }, + { + verb: bandawul, + result: { + type: "verb", + verb: bandawul, + voice: "active", + canChangeVoice: true, + transitivity: "transitive", + compound: "stative", + canBeGenStat: false, + canChangeTransGenTrans: false, + variableRs: false, + canChangeStatDyn: false, + negative: false, + tense: "presentVerb", + }, + }, + { + verb: bandedul, + result: { + type: "verb", + verb: bandedul, + voice: "active", + canChangeVoice: false, + transitivity: "intransitive", + compound: "stative", + canBeGenStat: false, + canChangeTransGenTrans: false, + variableRs: false, + canChangeStatDyn: false, + negative: false, + tense: "presentVerb", + }, + }, + { + verb: mundaWahul, + result: { + type: "verb", + verb: mundaWahul, + voice: "active", + canChangeVoice: true, + transitivity: "transitive", + compound: "dynamic", + canBeGenStat: false, + canChangeTransGenTrans: false, + variableRs: false, + canChangeStatDyn: false, + negative: false, + tense: "presentVerb", + dynAuxVerb: wahul, + }, + }, + { + verb: istreeKawul, + result: { + type: "verb", + verb: istreeKawul, + voice: "active", + canChangeVoice: true, + transitivity: "transitive", + compound: "stative", + canBeGenStat: false, + canChangeTransGenTrans: false, + variableRs: false, + canChangeStatDyn: true, + negative: false, + tense: "presentVerb", + dynAuxVerb: kawulDyn, + }, + }, + { + verb: cheeghKawul, + result: { + type: "verb", + verb: cheeghKawul, + voice: "active", + canChangeVoice: false, + transitivity: "transitive", + compound: "dynamic", + canBeGenStat: true, + canChangeTransGenTrans: false, + variableRs: false, + canChangeStatDyn: false, + negative: false, + tense: "presentVerb", + dynAuxVerb: kawulDyn, + }, + }, + { + verb: kaarKawul, + result: { + type: "verb", + verb: kaarKawul, + voice: "active", + canChangeVoice: false, + transitivity: "transitive", + compound: "dynamic", + canBeGenStat: false, + canChangeTransGenTrans: false, + variableRs: false, + canChangeStatDyn: false, + negative: false, + tense: "presentVerb", + dynAuxVerb: kawulDyn, + }, + }, +]; + +test("verb selection", () => { + tests.forEach(({ verb, result }) => { + expect(makeVerbSelection(verb)) + .toEqual(result); + }); +}); + +test("verb selection failures", () => { + expect(() => { + makeVerbSelection(wahulNoC); + }).toThrow(); + expect(() => { + makeVerbSelection(wahulNoC2); + }).toThrow(); +}); + + diff --git a/src/lib/src/new-verb-engine/make-verb-selection.ts b/src/lib/src/new-verb-engine/make-verb-selection.ts new file mode 100644 index 0000000..136cd2e --- /dev/null +++ b/src/lib/src/new-verb-engine/make-verb-selection.ts @@ -0,0 +1,107 @@ +import * as T from "../../../types"; +import { removeFVarientsFromVerb } from "../accent-and-ps-utils"; +import { dynamicAuxVerbs } from "../dyn-comp-aux-verbs"; + +export function makeVerbSelection(verb: T.VerbEntry): T.NewVerbSelection { + const v = removeFVarientsFromVerb(verb); + const { + transitivity, + canChangeTransGenTrans, + } = getTransitivity(v); + const compInfo = getCompoundInfo(v); + const canChangeVoice = transitivity === "transitive" && (compInfo.dynAuxVerb?.entry.p !== "کول" || compInfo.canChangeStatDyn) && !compInfo.canBeGenStat; + return { + type: "verb", + verb: v, + transitivity, + canChangeTransGenTrans, + ...compInfo, + tense: "presentVerb", + voice: "active", + canChangeVoice, + negative: false, + variableRs: false, + } +} + +function getCompoundInfo(v: T.VerbEntryNoFVars): { + compound: T.NewVerbSelection["compound"], + canChangeStatDyn: boolean, + dynAuxVerb?: T.NewVerbSelection["dynAuxVerb"], + canBeGenStat: boolean, +} { + const c = v.entry.c; + if (c.includes(" stat. comp.")) { + return { + compound: "stative", + canChangeStatDyn: false, + canBeGenStat: false, + }; + } + if (c.includes(" dyn. comp.")) { + return { + compound: "dynamic", + canChangeStatDyn: false, + dynAuxVerb: getDynamicAuxVerb(v), + canBeGenStat: false, + }; + } + if (c.includes(" dyn./stat. comp.")) { + return { + compound: "stative", + canChangeStatDyn: true, + dynAuxVerb: getDynamicAuxVerb(v), + canBeGenStat: false, + }; + } + if (c.includes(" gen. stat./dyn. comp.")) { + return { + compound: "dynamic", + canBeGenStat: true, + dynAuxVerb: getDynamicAuxVerb(v), + canChangeStatDyn: false, + }; + } + return { + compound: false, + canChangeStatDyn: false, + canBeGenStat: false, + }; +} + +function getTransitivity(v: T.VerbEntryNoFVars): { + transitivity: T.Transitivity, + canChangeTransGenTrans: boolean, +} { + const c = v.entry.c || ""; + const canChangeTransGenTrans = c.includes(" trans./gramm. trans."); + if (c.includes(" intrans.")) { + return { + canChangeTransGenTrans, + transitivity: "intransitive", + }; + } + if (c.includes("gramm. trans") && !canChangeTransGenTrans) { + return { + canChangeTransGenTrans, + transitivity: "grammatically transitive", + }; + } + if (c.includes(" trans.")) { + return { + canChangeTransGenTrans, + transitivity: "transitive", + }; + } + throw new Error("invalid or missing transitivity info in verb c"); +} + +function getDynamicAuxVerb({ entry }: T.VerbEntryNoFVars): T.VerbEntryNoFVars { + const auxWord = entry.p.trim().split(" ").slice(-1)[0]; + const auxWordResult: T.VerbEntry | undefined = dynamicAuxVerbs.find((a) => a.entry.p === auxWord) as T.VerbEntry; + /* istanbul ignore next */ + if (!auxWordResult) { + throw new Error("unknown auxilary verb for dynamic compound"); + } + return removeFVarientsFromVerb(auxWordResult); +} \ No newline at end of file diff --git a/src/lib/src/new-verb-engine/render-verb.test.ts b/src/lib/src/new-verb-engine/render-verb.test.ts index 00eedc6..a910ba7 100644 --- a/src/lib/src/new-verb-engine/render-verb.test.ts +++ b/src/lib/src/new-verb-engine/render-verb.test.ts @@ -1,6 +1,7 @@ import { renderVerb } from "./render-verb"; import { vEntry } from "./rs-helpers"; import * as T from "../../../types"; +import { negate } from "rambda"; 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 raawrul = vEntry({"ts":1527815214,"i":6954,"p":"راوړل","f":"raawRúl","g":"raawRul","e":"to bring, deliver (inanimate objects)","r":4,"c":"v. trans.","tppp":"راووړ","tppf":"raawoR","noOo":true,"separationAtP":2,"separationAtF":3,"ec":"bring,brings,bringing,brought,brought"}); @@ -52,6 +53,7 @@ test("basic tenses", () => { tense: "presentVerb", person: T.Person.FirstSingMale, voice: "active", + negative: false, })).toEqual({ hasBa: false, @@ -67,6 +69,7 @@ test("basic tenses", () => { tense: "subjunctiveVerb", person: T.Person.SecondSingMale, voice: "active", + negative: false, })).toEqual({ hasBa: false, vbs: [ @@ -81,6 +84,7 @@ test("basic tenses", () => { tense: "habitualPerfectivePast", person: T.Person.ThirdSingFemale, voice: "active", + negative: false, })).toEqual({ hasBa: true, vbs: [ @@ -106,6 +110,7 @@ test("basic tenses with inflecting roots/stems", () => { person: T.Person.FirstSingMale, presObj: T.Person.ThirdSingFemale, voice: "active", + negative: false, })).toEqual({ hasBa: false, vbs: [ @@ -517,6 +522,7 @@ test("special endings", () => { tense: x.tense, person: T.Person.ThirdSingMale, voice: "active", + negative: false, })).toEqual({ hasBa: false, vbs: x.result }); }); @@ -525,6 +531,7 @@ test("special endings", () => { tense: "perfectivePast", person: T.Person.FirstPlurMale, voice: "active", + negative: false, })).toEqual({ hasBa: false, vbs: [ @@ -546,6 +553,7 @@ test("special endings", () => { tense: "imperfectivePast", person: T.Person.FirstSingMale, voice: "active", + negative: false, })).toEqual({ hasBa: false, vbs: [ @@ -568,6 +576,7 @@ test("special endings", () => { tense: "imperfectivePast", person: T.Person.ThirdSingFemale, voice: "active", + negative: false, })).toEqual({ hasBa: false, vbs: [ @@ -591,6 +600,7 @@ test("special endings", () => { tense: "imperfectivePast", person: T.Person.ThirdPlurMale, voice: "active", + negative: false, })).toEqual({ hasBa: false, vbs: [ @@ -612,6 +622,7 @@ test("special endings", () => { tense: "perfectivePast", person: T.Person.ThirdPlurMale, voice: "active", + negative: false, })).toEqual({ hasBa: false, vbs: [ @@ -636,6 +647,7 @@ test("imperative tenses", () => { tense: "imperfectiveImperative", person: T.Person.SecondSingMale, voice: "active", + negative: false, })).toEqual({ hasBa: false, vbs: [ @@ -650,6 +662,7 @@ test("imperative tenses", () => { tense: "perfectiveImperative", person: T.Person.SecondSingFemale, voice: "active", + negative: false, })).toEqual({ hasBa: false, vbs: [ @@ -664,6 +677,7 @@ test("imperative tenses", () => { tense: "imperfectiveImperative", person: T.Person.SecondPlurMale, voice: "active", + negative: false, })).toEqual({ hasBa: false, vbs: [ @@ -678,6 +692,7 @@ test("imperative tenses", () => { tense: "perfectiveImperative", person: T.Person.SecondPlurFemale, voice: "active", + negative: false, })).toEqual({ hasBa: false, vbs: [ @@ -695,6 +710,7 @@ test("ability tenses", () => { tense: "presentVerbModal", person: T.Person.FirstSingMale, voice: "active", + negative: false, })).toEqual({ hasBa: false, vbs: [ @@ -729,6 +745,7 @@ test("basic tenses", () => { tense: "presentVerb", person: T.Person.FirstSingMale, voice: "active", + negative: false, })).toEqual({ hasBa: false, @@ -744,6 +761,7 @@ test("basic tenses", () => { tense: "subjunctiveVerb", person: T.Person.SecondSingMale, voice: "active", + negative: false, })).toEqual({ hasBa: false, vbs: [ @@ -758,6 +776,7 @@ test("basic tenses", () => { tense: "habitualPerfectivePast", person: T.Person.ThirdSingFemale, voice: "active", + negative: false, })).toEqual({ hasBa: true, vbs: [ @@ -782,6 +801,7 @@ test("perfect tenses", () => { tense: "presentPerfect", person: T.Person.FirstSingMale, voice: "active", + negative: false, })).toEqual({ hasBa: false, vbs: [ @@ -797,6 +817,7 @@ test("perfect tenses", () => { tense: "subjunctivePerfect", person: T.Person.FirstSingMale, voice: "active", + negative: false, })).toEqual({ hasBa: false, vbs: [ @@ -812,6 +833,7 @@ test("perfect tenses", () => { tense: "habitualPerfect", person: T.Person.FirstSingMale, voice: "active", + negative: false, })).toEqual({ hasBa: false, vbs: [ @@ -827,6 +849,7 @@ test("perfect tenses", () => { tense: "habitualPerfect", person: T.Person.ThirdPlurMale, voice: "active", + negative: false, })).toEqual({ hasBa: false, vbs: [ @@ -842,6 +865,7 @@ test("perfect tenses", () => { tense: "futurePerfect", person: T.Person.FirstSingMale, voice: "active", + negative: false, })).toEqual({ hasBa: true, vbs: [ @@ -857,6 +881,7 @@ test("perfect tenses", () => { tense: "pastPerfect", person: T.Person.SecondSingFemale, voice: "active", + negative: false, })).toEqual({ hasBa: false, vbs: [ @@ -879,6 +904,7 @@ test("perfect tenses", () => { tense: "wouldBePerfect", person: T.Person.SecondSingFemale, voice: "active", + negative: false, })).toEqual({ hasBa: true, vbs: [ @@ -901,6 +927,7 @@ test("perfect tenses", () => { tense: "pastSubjunctivePerfect", person: T.Person.SecondSingFemale, voice: "active", + negative: false, })).toEqual({ hasBa: false, vbs: [ @@ -920,6 +947,7 @@ test("perfect tenses", () => { tense: "wouldHaveBeenPerfect", person: T.Person.SecondSingFemale, voice: "active", + negative: false, })).toEqual({ hasBa: true, vbs: [ @@ -942,7 +970,8 @@ test("ending on complex verbs", () => { tense: "presentVerbModal", person: T.Person.SecondSingMale, voice: "active", - presObj: T.Person.ThirdSingFemale + presObj: T.Person.ThirdSingFemale, + negative: false, })).toEqual({ hasBa: false, vbs: [ @@ -986,7 +1015,8 @@ test("ending on complex verbs", () => { tense: "presentVerb", person: T.Person.SecondSingMale, voice: "active", - presObj: T.Person.ThirdSingFemale + presObj: T.Person.ThirdSingFemale, + negative: false, })).toEqual({ hasBa: false, vbs: [ diff --git a/src/lib/src/new-verb-engine/render-verb.ts b/src/lib/src/new-verb-engine/render-verb.ts index 4057ef3..1c95c0c 100644 --- a/src/lib/src/new-verb-engine/render-verb.ts +++ b/src/lib/src/new-verb-engine/render-verb.ts @@ -24,8 +24,9 @@ import { getAspect, isKedul, perfectTenseToEquative, verbEndingConcat } from "./ import { accentOnNFromEnd, accentPsSyllable, removeAccents } from "../accent-helpers"; // TODO: problem with laaR - no perfective split -export function renderVerb({ verb, tense, person, voice, presObj }: { +export function renderVerb({ verb, tense, person, voice, presObj, negative }: { verb: T.VerbEntry, + negative: boolean, tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense, // TODO: make T.Tense person: T.Person, voice: T.Voice, @@ -45,7 +46,7 @@ export function renderVerb({ verb, tense, person, voice, presObj }: { gender: personGender(rootPerson), number: personNumber(rootPerson), }; - const aspect = getAspect(tense); + const aspect = getAspect(tense, negative); const isImperative = isImperativeTense(tense); const type = isAbilityTense(tense) ? "ability" : "basic"; diff --git a/src/lib/src/new-verb-engine/rs-helpers.ts b/src/lib/src/new-verb-engine/rs-helpers.ts index 48ecb6c..bacf1cb 100644 --- a/src/lib/src/new-verb-engine/rs-helpers.ts +++ b/src/lib/src/new-verb-engine/rs-helpers.ts @@ -5,7 +5,7 @@ import { concatPsString, isUnisexSet, psStringFromEntry, trimOffPs } from "../p- import { inflectPattern1 } from "./new-inflectors"; import { getLength } from "../p-text-helpers"; import { equativeEndings } from "../grammar-units"; -import { isAdjectiveEntry } from "../type-predicates"; +import { isAdjectiveEntry, isImperativeTense } from "../type-predicates"; import { inflectWord } from "../pashto-inflector"; export function isStatComp(v: T.VerbEntry): boolean { @@ -273,7 +273,10 @@ export function getLongVB(vb: T.VBA): T.VBNoLenghts { }; } -export function getAspect(tense: T.VerbTense | T.AbilityTense | T.ImperativeTense): T.Aspect { +export function getAspect(tense: T.VerbTense | T.AbilityTense | T.ImperativeTense, negative: boolean): T.Aspect { + if (isImperativeTense(tense) && negative) { + return "imperfective"; + } const t = tense.replace("Modal", ""); const imperfectives: Parameters[0][] = ["presentVerb", "imperfectiveFuture", "imperfectivePast", "habitualImperfectivePast", "imperfectiveImperative"]; if (imperfectives.includes(t as Parameters[0])) { diff --git a/src/lib/src/phrase-building/blocks-utils.ts b/src/lib/src/phrase-building/blocks-utils.ts index f3d3ac8..041a92a 100644 --- a/src/lib/src/phrase-building/blocks-utils.ts +++ b/src/lib/src/phrase-building/blocks-utils.ts @@ -1,34 +1,6 @@ import * as T from "../../../types"; import { getLength } from "../p-text-helpers"; -export function isRenderedVerbB(block: T.Block["block"]): block is T.RenderedVerbB { - if (block.type === "modalVerbBlock") { - return true; - } - if (block.type === "modalVerbKedulPart") { - return true; - } - if (block.type === "perfectEquativeBlock") { - return true; - } - if (block.type === "perfectParticipleBlock") { - return true; - } - if (block.type === "perfectiveHead") { - return true; - } - if (block.type === "verb") { - return true; - } - return false; -} - -export function getVerbBlocks(blocks: T.Block[][]): T.RenderedVerbB[] { - return blocks[0] - .filter(b => isRenderedVerbB(b.block)) - .map(b => b.block) as T.RenderedVerbB[]; -} - export function makeBlock(block: T.Block["block"], key?: number): T.Block { return { key: key === undefined ? Math.random() : key, @@ -53,15 +25,6 @@ export function getSubjectSelection(blocks: T.EPSBlock[] | T.EPSBlockComplete[] return b.block; } -export function findPerfectiveHead(blocks: T.Block[][]): T.PerfectiveHeadBlock | undefined { - - const b = blocks[0].find(f => f.block.type === "perfectiveHead"); - if (!b || b.block.type !== "perfectiveHead") { - return undefined; - } - return b.block; -} - export function getSubjectSelectionFromBlocks(blocks: T.Block[][]): T.Rendered { const b = blocks[0].find(f => f.block.type === "subjectSelection"); if (!b || b.block.type !== "subjectSelection") { @@ -78,49 +41,6 @@ export function getObjectSelectionFromBlocks(blocks: T.Block[][]): T.Rendered f.block.type === "verb"); - const p = blocks[0].find(f => f.block.type === "perfectParticipleBlock"); - const m = blocks[0].find(f => f.block.type === "modalVerbBlock"); - const v = (b && b.block.type === "verb") - ? b.block - : (p && p.block.type === "perfectParticipleBlock") - ? p.block.verb - : (m && m.block.type === "modalVerbBlock") - ? m.block.verb - : undefined; - if (!v) { - throw new Error("verbSelection not found in blocks"); - } - return v; -} - -export function getLengthyFromBlocks(blocks: T.Block[][]): T.VerbRenderedBlock | T.PerfectParticipleBlock | T.ModalVerbBlock | T.PerfectEquativeBlock | undefined { - return blocks[0].find(f => f.block.type === "verb" - || - f.block.type === "perfectParticipleBlock" - || - f.block.type === "modalVerbBlock" - || - f.block.type === "perfectEquativeBlock" - )?.block as T.VerbRenderedBlock | T.PerfectParticipleBlock | T.ModalVerbBlock | T.PerfectEquativeBlock | undefined; -} - -export function getVerbAndHeadFromBlocks(blocks: T.Block[][]): { verb: T.VerbRenderedBlock, perfectiveHead: T.PerfectiveHeadBlock | undefined } { - const verb = getVerbFromBlocks(blocks); - const perfectiveHead = blocks[0].find(f => f.block.type === "perfectiveHead"); - if (!perfectiveHead || perfectiveHead.block.type !== "perfectiveHead") { - return { - verb, - perfectiveHead: undefined, - }; - } - return { - verb, - perfectiveHead: perfectiveHead.block, - }; -} - export function includesShrunkenServant(kids?: T.Kid[]): boolean { if (!kids) return false; return kids.some(k => ( @@ -140,19 +60,6 @@ export function getAPsFromBlocks(blocks: T.Block[][]): T.Rendered return blocks[0].filter(b => b.block.type === "AP").map(b => b.block) as T.Rendered[]; } -export function getComplementFromBlocks(blocks: T.Block[][]): T.Rendered | T.Rendered | undefined { - const complement = blocks[0].find(b => b.block.type === "complement")?.block as T.Rendered | T.Rendered | undefined; - if (complement) { - return complement; - } - // maybe there's a complement in the verb block - const verb = getVerbFromBlocks(blocks); - if (verb?.block.complementWelded) { - return verb.block.complementWelded; - } - return undefined; -} - export function getObjectSelection(blocks: T.VPSBlockComplete[]): T.ObjectSelectionComplete; export function getObjectSelection(blocks: T.VPSBlock[]): T.ObjectSelection; export function getObjectSelection(blocks: T.VPSBlock[] | T.VPSBlockComplete[]): T.ObjectSelection | T.ObjectSelectionComplete { @@ -337,56 +244,26 @@ export function specifyEquativeLength(blocksWVars: T.Block[][], length: "long" | return blocksWVars.map(specify); } -export function specifyBlockLength(blocksWVars: T.Block[][], length: "long" | "short" | "mini"): T.Block[][] { - function specify(blocks: T.Block[]): T.Block[] { - return blocks.map((block): T.Block => { - if (block.block.type === "verb") { - const v: T.Block = { - ...block, - block: { - ...block.block, - block: { - ...block.block.block, - ps: getLength(block.block.block.ps, length), - }, - }, - }; - return v; - } - if (block.block.type === "perfectEquativeBlock") { - const v: T.Block = { - ...block, - block: { - ...block.block, - ps: getLength(block.block.ps, length), - }, - }; - return v; - } - if (block.block.type === "perfectParticipleBlock") { - const p: T.Block = { - ...block, - block: { - ...block.block, - ps: getLength(block.block.ps, length), - }, - }; - return p; - } - if (block.block.type === "modalVerbBlock") { - const m: T.Block = { - ...block, - block: { - ...block.block, - ps: getLength(block.block.ps, length), - }, - }; - return m; - } - return block; - }); +export function isRenderedVerbB({ block }: T.Block): boolean { + if (block.type === "equative") { + return true; } - return blocksWVars.map(specify); + if (block.type === "VB") { + return true; + } + if (block.type === "PH") { + return true; + } + if (block.type === "NComp") { + return true; + } + if (block.type === "welded") { + return true; + } + if (block.type === "complement") { + return true; + } + return false } export function hasEquativeWithLengths(blocks: T.Block[][]): boolean { @@ -396,18 +273,6 @@ export function hasEquativeWithLengths(blocks: T.Block[][]): boolean { return "long" in equative.block.equative.ps; } -export function hasVerbWithLengths(blocks: T.Block[][]): boolean { - // TODO: handle length options with perfect verb equative as well? - const verb = blocks[0].find(x => (x.block.type === "verb" || x.block.type === "perfectParticipleBlock" || x.block.type === "modalVerbBlock")); - if (!verb) throw new Error("verb not found in blocks"); - if (verb.block.type !== "verb" && verb.block.type !== "perfectParticipleBlock" && verb.block.type !== "modalVerbBlock") throw new Error("error finding verb in blocks"); - return ( - (verb.block.type === "verb" && "long" in verb.block.block.ps) - || (verb.block.type === "perfectParticipleBlock" && "long" in verb.block.ps) - || (verb.block.type === "modalVerbBlock" && "long" in verb.block.ps) - ); -} - function arrayMove(ar: X[], old_index: number, new_index: number): X[] { const arr = [...ar]; const new_i = (new_index >= arr.length) diff --git a/src/lib/src/phrase-building/compile.ts b/src/lib/src/phrase-building/compile.ts index 8402434..0dda645 100644 --- a/src/lib/src/phrase-building/compile.ts +++ b/src/lib/src/phrase-building/compile.ts @@ -11,18 +11,14 @@ import { import { getEnglishFromRendered, getPashtoFromRendered } from "./np-tools"; import { completeEPSelection, renderEP } from "./render-ep"; import { completeVPSelection } from "./vp-tools"; -import { isStativeHelper, renderVP } from "./render-vp"; +import { renderVP } from "./render-vp"; import { getAPsFromBlocks, - getComplementFromBlocks, - getLengthyFromBlocks, getObjectSelectionFromBlocks, getPredicateSelectionFromBlocks, getSubjectSelectionFromBlocks, - getVerbFromBlocks, hasEquativeWithLengths, isRenderedVerbB, - specifyBlockLength, specifyEquativeLength, } from "./blocks-utils"; import { @@ -77,29 +73,16 @@ export function compileEP(EP: T.EPRendered, combineLengths?: boolean, blankOut?: export function compileVP(VP: T.VPRendered, form: T.FormVersion): { ps: T.SingleOrLengthOpts, e?: string [] }; export function compileVP(VP: T.VPRendered, form: T.FormVersion, combineLengths: true, blankOut?: BlankoutOptions): { ps: T.PsString[], e?: string [] }; export function compileVP(VP: T.VPRendered, form: T.FormVersion, combineLengths?: true, blankOut?: BlankoutOptions): { ps: T.SingleOrLengthOpts, e?: string [] } { - const verb = getVerbFromBlocks(VP.blocks).block; + // const verb = getVerbFromBlocks(VP.blocks).block; const psResult = compileVPPs(VP.blocks, VP.kids, form, VP.king, blankOut); return { ps: combineLengths ? flattenLengths(psResult) : psResult, // TODO: English doesn't quite work for dynamic compounds in passive voice - e: (verb.voice === "passive" && VP.isCompound === "dynamic") ? undefined : compileEnglishVP(VP), + e: /* (verb.voice === "passive" && VP.isCompound === "dynamic") ? undefined : */ compileEnglishVP(VP), }; } -function compileVPPs(blocks: T.Block[][], kids: T.Kid[], form: T.FormVersion, king: "subject" | "object", blankOut?: BlankoutOptions): T.SingleOrLengthOpts { - const lengthyBlock = getLengthyFromBlocks(blocks); - const potentialLengthy = lengthyBlock?.type === "verb" - ? lengthyBlock.block.ps - : lengthyBlock?.ps; - if (potentialLengthy && "long" in potentialLengthy) { - return { - long: compileVPPs(specifyBlockLength(blocks, "long"), kids, form, king, blankOut) as T.PsString[], - short: compileVPPs(specifyBlockLength(blocks, "short"), kids, form, king, blankOut) as T.PsString[], - ..."mini" in potentialLengthy ? { - mini: compileVPPs(specifyBlockLength(blocks, "mini"), kids, form, king, blankOut) as T.PsString[], - } : {}, - }; - } +function compileVPPs(blocks: T.Block[][], kids: T.Kid[], form: T.FormVersion, king: "subject" | "object", blankOut?: BlankoutOptions): T.PsString[] { const subjectPerson = getSubjectSelectionFromBlocks(blocks) .selection.selection.person; const blocksWKids = putKidsInKidsSection( @@ -176,7 +159,7 @@ function combineIntoText(piecesWVars: (T.Block | T.Kid | T.PsString)[][], subjec ? [first] : ( (blankOut?.equative && "block" in first && first.block.type === "equative") || - (blankOut?.verb && "block" in first && isRenderedVerbB(first.block)) || + (blankOut?.verb && "block" in first && isRenderedVerbB(first)) || (blankOut?.predicate && "block" in first && first.block.type === "predicateSelection") ) ? [blank] @@ -192,7 +175,7 @@ function combineIntoText(piecesWVars: (T.Block | T.Kid | T.PsString)[][], subjec firstPs.map(fPs => concatPsString( fPs, // TODO: this spacing is a mess and not accurate - (!("p" in first) && "block" in first && first.block.type === "perfectiveHead" && !("p" in next) && (("block" in next && (next.block.type === "verb" || next.block.type === "negative" || next.block.type === "modalVerbBlock")) || ("kid" in next && next.kid.type === "mini-pronoun"))) + (!("p" in first) && "block" in first && first.block.type === "PH" && !("p" in next) && (("block" in next && (isRenderedVerbB(next) || next.block.type === "negative")) || ("kid" in next && next.kid.type === "mini-pronoun"))) ? ((("block" in next && next.block.type === "negative") || ("kid" in next && next.kid.type === "mini-pronoun")) ? { p: "", f: " " } : "") : " ", r, @@ -220,46 +203,20 @@ function getPsFromPiece(piece: T.Block | T.Kid, subjectPerson: T.Person): T.PsSt if (piece.block.type === "AP") { return getPashtoFromRendered(piece.block, subjectPerson); } - if (piece.block.type === "perfectiveHead") { + if (piece.block.type === "PH") { return [piece.block.ps]; } + if (piece.block.type === "VB") { + return flattenLengths(piece.block.ps); + } if (piece.block.type === "objectSelection") { if (typeof piece.block.selection !== "object") { return [{ p: "", f: "" }]; } return getPashtoFromRendered(piece.block.selection, subjectPerson); } - if (piece.block.type === "verb") { - // getLong is just for type safety - we will have split up the length options earlier in compileVPPs - const verbPs = getLong(piece.block.block.ps); - if (piece.block.block.complementWelded) { - return combineComplementWVerbPs(piece.block.block.complementWelded, verbPs); - } - return verbPs; - } - if (piece.block.type === "perfectParticipleBlock") { - // getLong is just for type safety - we will have split up the length options earlier in compileVPPs - const verbPs = getLong(piece.block.ps); - if (piece.block.complementWelded) { - return combineComplementWVerbPs(piece.block.complementWelded, verbPs); - } - return verbPs; - } - if (piece.block.type === "perfectEquativeBlock") { - // just using the short one for now - it will only be short anyways - return getShort(piece.block.ps); - } - if (piece.block.type === "modalVerbBlock") { - // getLong is just for type safety - we will have split up the length options earlier in compileVPPs - const verbPs = getLong(piece.block.ps); - if (piece.block.complementWelded) { - return combineComplementWVerbPs(piece.block.complementWelded, verbPs); - } - return verbPs; - } - if (piece.block.type === "modalVerbKedulPart") { - // just using the short one for now - it will only be short anyways - return getShort(piece.block.ps); + if (piece.block.type === "NComp") { + return [piece.block.comp.ps]; } if (piece.block.type === "complement") { if (piece.block.selection.type === "sandwich") { @@ -268,6 +225,8 @@ function getPsFromPiece(piece: T.Block | T.Kid, subjectPerson: T.Person): T.PsSt } return piece.block.selection.ps; } + // welded + return getPsFromWelded(piece.block); } if ("kid" in piece) { if (piece.kid.type === "ba") { @@ -280,13 +239,17 @@ function getPsFromPiece(piece: T.Block | T.Kid, subjectPerson: T.Person): T.PsSt throw new Error("unrecognized piece type"); } -function combineComplementWVerbPs(comp: T.Rendered, v: T.PsString[]): T.PsString[] { - const compPs = comp.selection.type === "sandwich" - ? getPashtoFromRendered({ type: "AP", selection: comp.selection }, false) - : comp.selection.ps; - return compPs.flatMap((c) => ( - v.map((p) => concatPsString(c, " ", p)) - )); +function getPsFromWelded(v: T.Welded): T.PsString[] { + function getPsFromSide(v: T.VBBasic | T.Welded | T.NComp | T.VBGenNum): T.PsString[] { + if (v.type === "VB") { + return flattenLengths(v.ps); + } + if (v.type === "NComp") { + return [v.comp.ps]; + } + return getPsFromWelded(v); + } + return [...getPsFromSide(v.left), ...getPsFromSide(v.right)]; } function getEngAPs(blocks: T.Block[][]): string { @@ -298,15 +261,16 @@ function getEngAPs(blocks: T.Block[][]): string { } function getEngComplement(blocks: T.Block[][]): string | undefined { - const comp = getComplementFromBlocks(blocks); - if (!comp) return undefined; - if (comp.selection.type === "unselected") { - return "____"; - } - if (comp.selection.type === "sandwich") { - return getEnglishFromRendered({ type: "AP", selection: comp.selection }); - } - return comp.selection.e; + return "TODO"; + // const comp = getComplementFromBlocks(blocks); + // if (!comp) return undefined; + // if (comp.selection.type === "unselected") { + // return "____"; + // } + // if (comp.selection.type === "sandwich") { + // return getEnglishFromRendered({ type: "AP", selection: comp.selection }); + // } + // return comp.selection.e; } function putKidsInKidsSection(blocksWVars: T.Block[][], kids: T.Kid[], enforceKidsSectionBlankout: boolean): (T.Block | T.Kid | T.PsString)[][] { @@ -328,7 +292,8 @@ function compileEnglishVP(VP: T.VPRendered): string[] | undefined { .replace("$SUBJ", subject) .replace("$OBJ", object || "") // add the complement in English if it's an external complement from a helper verb (kawul/kedul) - + ((complement && isStativeHelper(getVerbFromBlocks(VP.blocks).block.verb)) + // TODO! + + (complement /* && isStativeHelper(getVerbFromBlocks(VP.blocks).block.verb))*/ ? ` ${complement}` : "") + APs; diff --git a/src/lib/src/phrase-building/new-render-verb.txt b/src/lib/src/phrase-building/new-render-verb.txt deleted file mode 100644 index 83d72e7..0000000 --- a/src/lib/src/phrase-building/new-render-verb.txt +++ /dev/null @@ -1,31 +0,0 @@ -import * as T from "../../../types"; - -function renderVerb(vs: T.VerbSelectionComplete): { - verbBlocks: VerbBlocks, - hasBa: boolean, -} { - const base = chooseRootOrStem(vs.tense, vs.verb); - const b = concatPsString(base, grammarUnits.presentEndings[0][0][0]); - return { - verbBlocks: [{ - type: "verb", - block: { - ...vs, - hasBa: false, - ps: [], - person: 0, - complementWelded: undefined, - } - }], - hasBa: false, - } -} - -function chooseRootOrStem(tense: T.VerbFormName, entry: T.VerbEntry): T.FullForm { - const info = getVerbInfo(entry.entry, entry.complement) - if ("stative" in info || "transitive" in info) { - throw new Error("multiple verb types not supported yet"); - } - return info.stem.imperfective - -} diff --git a/src/lib/src/phrase-building/render-vp.ts b/src/lib/src/phrase-building/render-vp.ts index e7833e8..a97397e 100644 --- a/src/lib/src/phrase-building/render-vp.ts +++ b/src/lib/src/phrase-building/render-vp.ts @@ -1,5 +1,6 @@ import * as T from "../../../types"; import { + applyToSingOrLengthOpts, getVerbBlockPosFromPerson, } from "../misc-helpers"; import { conjugateVerb } from "../verb-conjugation"; @@ -10,7 +11,7 @@ import { splitOffLeapfrogWordFull, getShort, } from "../p-text-helpers"; -import { removeAccents, removeAccentsWLength } from "../accent-helpers"; +import { removeAccents, removeAccentsWLength, removeVerbAccent } from "../accent-helpers"; import { getPersonFromNP, removeBa, @@ -27,10 +28,11 @@ import { isPerfectTense, isTlulVerb, } from "../type-predicates"; +import { renderVerb } from "../new-verb-engine/render-verb"; import { renderEnglishVPBase } from "./english-vp-rendering"; import { personGender } from "../misc-helpers"; import { renderNPSelection } from "./render-np"; -import { findPerfectiveHead, getObjectSelection, getSubjectSelection, makeBlock, makeKid } from "./blocks-utils"; +import { getObjectSelection, getSubjectSelection, makeBlock, makeKid } from "./blocks-utils"; import { renderAPSelection } from "./render-ap"; import { findPossesivesToShrink, orderKids, getMiniPronounPs } from "./render-common"; import { renderComplementSelection } from "./render-complement"; @@ -62,18 +64,23 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered { king, complementPerson, }); - const { verbBlocks, hasBa } = renderVerbSelection(VP.verb, kingPerson, objectPerson === undefined ? subjectPerson : objectPerson, VP.externalComplement); - const b: T.VPRendered = { + const { vbs, hasBa } = renderVerb({ + verb: VP.verb.verb, + tense: VP.verb.tense, + person: kingPerson, + voice: VP.verb.voice, + negative: VP.verb.negative, + }); + const VBwNeg = insertNegative(vbs, VP.verb.negative, isImperativeTense(VP.verb.tense)); + // just enter the negative in the verb blocks + return { type: "VPRendered", king, servant, isPast, isTransitive, isCompound: VP.verb.isCompound, - blocks: insertNegative([ - ...firstBlocks, - ...verbBlocks.map(makeBlock), - ], VP.verb.negative, isImperativeTense(VP.verb.tense)), + blocks: VBwNeg.map(VBvars => [...firstBlocks, ...VBvars]), kids: getVPKids(hasBa, VP.blocks, VP.form, king), englishBase: renderEnglishVPBase({ subjectPerson, @@ -83,7 +90,6 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered { form: VP.form, whatsAdjustable: whatsAdjustable(VP), }; - return b; } function getVPKids(hasBa: boolean, blocks: T.VPSBlockComplete[], form: T.FormVersion, king: "subject" | "object"): T.Kid[] { @@ -116,156 +122,66 @@ function removeAbbreviated(blocks: T.VPSBlockComplete[], form: T.FormVersion, ki }) } -function insertNegative(blocks: T.Block[], negative: boolean, imperative: boolean): T.Block[][] { - // TODO: proper arrange verb with negative and variations - // ALSO removing the accent from nu - if (!negative) return [blocks]; - const blocksA = removeVerbAccent(blocks); - const basic: T.Block[] = [ - ...blocksA.slice(0, blocks.length - 1), - makeBlock({ - type: "negative", - imperative, - }), - ...blocksA.slice(-1), - ]; - const nonStandardPerfectiveSplit = hasNonStandardPerfectiveSplit(blocks); - if (nonStandardPerfectiveSplit) { - return [ - basic, - [ - ...blocksA.slice(0, blocks.length - 2), - makeBlock({ type: "negative", imperative }), - ...blocksA.slice(-2, -1), // second last (perfective split) - ...blocksA.slice(-1), // last (verb) - ], - ]; +function insertNegative(blocks: T.VerbRenderedOutput, negative: boolean, imperative: boolean): T.Block[][] { + if (!negative) { + return [blocks.flat().map(makeBlock)]; }; - if (hasLeapFroggable(blocks)) { - const perfectiveHead = findPerfectiveHead([blocks]); - const perfectiveHeadReg = perfectiveHead && ["و", "وا"].includes(perfectiveHead.ps.p); - if (perfectiveHead) { + const blocksA = removeVerbAccent(blocks).flat().map(makeBlock); + const neg = makeBlock({ type: "negative", imperative }); + const nonStandPerfectiveSplit = hasNonStandardPerfectiveSplit(blocks); + if (blocks[1].length === 2) { + // swapped ending with negative for ability and perfect verb forms + if (nonStandPerfectiveSplit) { return [ - [ - ...blocksA.slice(0, blocks.length - 2), - makeBlock({ type: "negative", imperative }), - ...blocksA.slice(-1), // last - ...blocksA.slice(-2, -1), // second last - ], - [ - ...blocksA.slice(0, blocks.length - 2), - makeBlock({ type: "negative", imperative }), - ...blocksA.slice(-2), - ], - ...!perfectiveHeadReg ? [basic] : [], - ]; + insertFromEnd(swapEndingBlocks(blocksA), neg, 2), + insertFromEnd(swapEndingBlocks(blocksA, 2), neg, 3), + insertFromEnd(blocksA, neg, 1), + ] } return [ - [ - ...blocksA.slice(0, blocks.length - 2), - makeBlock({ type: "negative", imperative }), - ...blocksA.slice(-1), // last - ...blocksA.slice(-2, -1), // second last - ], - basic, + insertFromEnd(swapEndingBlocks(blocksA), neg, 2), + insertFromEnd(blocksA, neg, 1), ]; } - return [basic]; + if (nonStandPerfectiveSplit) { + return [ + insertFromEnd(blocksA, neg, 1), + insertFromEnd(blocksA, neg, 2), + ]; + } else { + return [insertFromEnd(blocksA, neg, 1)]; + } } -function hasLeapFroggable(blocks: T.Block[]): boolean { - return blocks.some(b => b.block.type === "perfectEquativeBlock" || b.block.type === "modalVerbBlock"); +function swapEndingBlocks(arr: X[], n: number = 1): X[] { + return [ + ...arr.slice(0, arr.length - (n + 1)), + ...arr.slice(-n), + ...arr.slice(-(n + 1), -n), + ]; } -function hasNonStandardPerfectiveSplit(blocks: T.Block[]): boolean { - const perfS = blocks.find(b => b.block.type === "perfectiveHead"); - if (!perfS || perfS.block.type !== "perfectiveHead") { +function insertFromEnd(arr: X[], x: X, n: number): X[] { + if (n === 0) { + return [...arr, x]; + } + return [ + ...arr.slice(0, arr.length - n), + x, + ...arr.slice(-n), + ]; +} + + + +function hasNonStandardPerfectiveSplit([[ph]]: T.VerbRenderedOutput): boolean { + if (!ph) { return false; } - return !["و", "وا"].includes(perfS.block.ps.p); -} - -function removeVerbAccent(blocks: T.Block[]): T.Block[] { - return blocks.map((block) => { - if (block.block.type === "perfectiveHead") { - return { - ...block, - block: { - ...block.block, - ps: removeAccents(block.block.ps), - }, - }; - } - if (block.block.type === "verb") { - return { - ...block, - block: { - ...block.block, - block: { - ...block.block.block, - ps: removeAccentsWLength(block.block.block.ps), - // The accent should ALREADY BE REMOVED FROM THE WELDED COMPLEMENT - BUT JUST TO BE SURE - ...block.block.block.complementWelded ? { - complementWelded: removeAccentFromWeldedComplement(block.block.block.complementWelded), - } : {}, - }, - }, - }; - } - return block; - }); -} - -function removeAccentFromWeldedComplement(complement: T.Rendered | T.Rendered): T.Rendered | T.Rendered { - if ( - complement.selection.type === "adjective" - || complement.selection.type === "loc. adv." - || complement.selection.type === "noun" - ) { - return { - ...complement, - selection: { - ...complement.selection, - ps: removeAccents(complement.selection.ps), - }, - }; + if (ph.type !== "PH") { + return false; } - if (complement.selection.type === "sandwich") { - return { - ...complement, - selection: { - ...complement.selection, - inside: removeAccentsFromNP(complement.selection.inside), - }, - }; - } - if (complement.selection.type === "unselected") { - return complement; - } - throw new Error("unexpected complement type"); -} - -function removeAccentsFromNP(np: T.Rendered): T.Rendered { - if (np.selection.type === "noun" || np.selection.type === "participle") { - return { - ...np, - selection: { - ...np.selection, - ps: removeAccents(np.selection.ps), - possesor: np.selection.possesor ? { - ...np.selection.possesor, - np: removeAccentsFromNP(np.selection.possesor.np), - } : undefined, - }, - }; - } - return { - ...np, - selection: { - ...np.selection, - ps: removeAccents(np.selection.ps), - }, - }; + return !["و", "وا"].includes(ph.ps.p); } function shrinkServant(np: T.NPSelection): T.MiniPronoun { @@ -353,328 +269,6 @@ function whatsAdjustable(VP: T.VPSelectionComplete): "both" | "king" | "servant" : "king"; } -type VerbBlocks = - | [T.PerfectiveHeadBlock, T.VerbRenderedBlock] // verb w perfective split - | [T.VerbRenderedBlock] // verb w/out perfective split - | [T.PerfectParticipleBlock, T.PerfectEquativeBlock] // perfect verb - | [T.ModalVerbBlock, T.ModalVerbKedulPart] // modal verb - - -function renderVerbSelection(vs: T.VerbSelectionComplete, person: T.Person, complementPerson: T.Person | undefined, externalComplement: T.ComplementSelection | T.UnselectedComplementSelection | undefined): { - verbBlocks: VerbBlocks - hasBa: boolean, -} { - const v = vs.dynAuxVerb || vs.verb; - const conjugations = conjugateVerb(v.entry, v.complement); - // TODO: error handle this? - const conj = "grammaticallyTransitive" in conjugations - // if there's an option for grammatically transitive or transitive - // will default to transitive - ? conjugations.transitive - : "stative" in conjugations - ? conjugations.stative - : conjugations; - const { ps: { head, rest }, hasBa } = getPsVerbConjugation(conj, vs, person, complementPerson); - const perfective = isPerfective(vs.tense); - const stativeHelper = isStativeHelper(vs.verb); - const complement: T.ComplementSelection | T.UnselectedComplementSelection | undefined = externalComplement - ? externalComplement - : vs.isCompound === "stative" - ? createComplementRetroactively(vs.verb.complement, complementPerson) - : undefined; - const renderedComplement = complement - ? renderComplementSelection(complement, complementPerson || T.Person.FirstSingMale) - : undefined; - const vrb: T.VerbRenderedBlock = { - type: "verb", - block: { - ...vs, - ps: (!perfective && renderedComplement && vs.verb.entry.p.includes(" ")) - ? removeComplement(rest, renderedComplement) - : rest, - person, - hasBa, - complementWelded: ((!perfective && renderedComplement) && ( - // it's a stative helper (kawul/kedul) with an external complement - (stativeHelper && externalComplement) - || - // it's a stative compound with a space - (vs.verb.entry.p.includes(" ")) - )) - ? removeAccentFromWeldedComplement(renderedComplement) - : undefined, - }, - }; - const verbBlocks = [ - ...(head ? ( - (!!(vs.isCompound === "stative" && renderedComplement)) ? [ - renderedComplement, - ] : [{ - type: "perfectiveHead", - ps: head, - } as T.PerfectiveHeadBlock] - ) : [] as [T.Rendered] | [T.PerfectiveHeadBlock] | []), - ...(externalComplement && perfective && stativeHelper) - ? [renderComplementSelection(externalComplement, complementPerson || T.Person.FirstSingMale)] - : [], - ...splitUpIfModal(vrb), - ] as VerbBlocks; - const perfectStuff = isPerfectTense(vrb.block.tense) ? getPerfectStuff(vrb) : undefined; - return { - verbBlocks: perfectStuff ? perfectStuff : verbBlocks, - hasBa, - }; -} - -function removeComplement(ps: T.SingleOrLengthOpts, complement: T.Rendered): T.SingleOrLengthOpts { - if ("long" in ps) { - return { - long: removeComplement(ps.long, complement) as T.PsString[], - short: removeComplement(ps.short, complement) as T.PsString[], - ...ps.mini ? { - mini: removeComplement(ps.mini, complement) as T.PsString[], - } : {}, - }; - } - const c = (complement.selection.type === "adjective" - ? complement.selection.ps - : complement.selection.type === "loc. adv." - ? complement.selection.ps - : complement.selection.type === "sandwich" - ? complement.selection.inside.selection.ps - : complement.selection.type === "noun" - ? complement.selection.ps - : complement.selection.ps); - // TODO: this is brutal - we could avoid this mess by redoing the verb conjugation engine - // to produce individual RenderedVerb objects instead of these tables with the complements in a string etc - const removed = ps.map(p => ( - c.reduce((acc, v) => { - return { - p: acc.p.replace(`${v.p} `, ""), - // without accent sensitivity in the matching - // because the complement may or may not have had the accent removed - f: acc.f.replace(`${v.f} `, "") - .replace(`${removeAccents(v.f)} `, ""), - } - }, p) - )); - return removed; -} - -function createComplementRetroactively(complement: T.DictionaryEntry | undefined, person: T.Person | undefined): T.ComplementSelection { - // This is a bit of a hack to work with the current verb conjugation system. - if (!complement) { - throw new Error("error creating complement from head - no complement in verb entry"); - } - // very important to have this before isNounEntry! (ie لېونی - n. m. anim unisex / adj. needs to be recognized as an adjective first!) - if (isAdjectiveEntry(complement)) { - if (person === undefined) { - throw new Error("there needs to be a complement person for rendering an adjective complement"); - } - return { - type: "complement", - selection: { - type: "adjective", - entry: complement, - sandwich: undefined, - }, - }; - } - if (isNounEntry(complement)) { - return { - type: "complement", - selection: makeNounSelection(complement, undefined, undefined), - }; - } - if (isLocativeAdverbEntry(complement)) { - return { - type: "complement", - selection: { - type: "loc. adv.", - entry: complement, - }, - }; - } - throw new Error("unsupported complement type"); -} - -export function isStativeHelper(v: T.VerbEntry): boolean { - if (v.entry.p === "کول" && v.entry.e.includes("make")) return true; - if (v.entry.p === "کېدل" && v.entry.e.includes("become")) return true; - return false; -} - -function splitUpIfModal(v: T.VerbRenderedBlock): [T.VerbRenderedBlock] | [T.ModalVerbBlock, T.ModalVerbKedulPart] { - if (!isAbilityTense(v.block.tense)) { - return [v]; - } - const [vrb, k] = splitOffLeapfrogWordFull(v.block.ps); - return [ - { - type: "modalVerbBlock", - ps: vrb, - verb: v, - complementWelded: v.block.complementWelded, - }, - { - type: "modalVerbKedulPart", - // sadly just for type safety - the conjugator always just gives us the short form - ps: getShort(k), - verb: v, - }, - ]; -} - -function getPerfectStuff(vrb: T.VerbRenderedBlock): [T.PerfectParticipleBlock, T.PerfectEquativeBlock] { - const [p, eq] = splitOffLeapfrogWordFull(vrb.block.ps); - return [ - { - type: "perfectParticipleBlock", - ps: p, - person: vrb.block.person, - verb: vrb, - complementWelded: vrb.block.complementWelded, - }, - { - type: "perfectEquativeBlock", - // TODO: right now the conjugator just always spits out the short form of the equative - would be nice to have both - ps: getShort(eq), - person: vrb.block.person, - }, - ]; -} - -function getPsVerbConjugation(conj: T.VerbConjugation, vs: T.VerbSelectionComplete, person: T.Person, objectPerson: T.Person | undefined): { - ps: { - head: T.PsString | undefined, - rest: T.SingleOrLengthOpts, - }, - hasBa: boolean, -} { - // TODO: handle the imperative form here - const f = getTenseVerbForm(conj, vs.tense, vs.voice, "phrase-building", vs.negative); - const block = getMatrixBlock(f, objectPerson, person); - const perfective = (vs.tense === "perfectiveImperative" && vs.negative) - ? false - : isPerfective(vs.tense); - const verbForm = getVerbFromBlock(block, person); - const hasBa = hasBaParticle(getLong(verbForm)[0]); - if (perfective) { - const past = isPastTense(vs.tense); - const splitInfo = conj.info[(past || isAbilityTense(vs.tense)) ? "root" : "stem"].perfectiveSplit; - if (!splitInfo || (isTlulVerb(vs.verb.entry) && isAbilityTense(vs.tense))) { - return { ps: { head: undefined, rest: removeBaFromForm(verbForm) }, hasBa }; - } - // TODO: Either solve this in the inflector or here, it seems silly (or redundant) - // to have a length option in the perfective split stem?? - const [splitHead] = getLong(getMatrixBlock(splitInfo, objectPerson, person)); - const ps = getHeadAndRest(splitHead, verbForm); - return { - hasBa, - ps, - }; - } - return { hasBa, ps: { head: undefined, rest: removeBaFromForm(verbForm) }}; -} - -function removeBaFromForm(f: T.SingleOrLengthOpts): T.SingleOrLengthOpts { - if ("long" in f) { - return { - long: removeBaFromForm(f.long) as T.PsString[], - short: removeBaFromForm(f.short) as T.PsString[], - ...f.mini ? { - mini: removeBaFromForm(f.mini) as T.PsString[], - } : {}, - }; - } - return f.map(removeBa); -} - -function getVerbFromBlock(block: T.SingleOrLengthOpts, person: T.Person): T.SingleOrLengthOpts { - function grabFromBlock(b: T.VerbBlock | T.ImperativeBlock, [row, col]: [ row: number, col: number ]): T.PsString[] { - if (isImperativeBlock(b)) { - return b[personGender(person) === "masc" ? 0 : 1][col]; - } - return b[row][col]; - } - const pos = getVerbBlockPosFromPerson(person); - if ("long" in block) { - return { - long: grabFromBlock(block.long, pos), - short: grabFromBlock(block.short, pos), - ...block.mini ? { - mini: grabFromBlock(block.mini, pos), - } : {}, - }; - } - return grabFromBlock(block, pos); -} - -function getHeadAndRest(head: T.PsString, rest: T.PsString[]): { head: T.PsString | undefined, rest: T.PsString[] }; -function getHeadAndRest(head: T.PsString, rest: T.SingleOrLengthOpts): { head: T.PsString | undefined, rest: T.SingleOrLengthOpts }; -function getHeadAndRest(head: T.PsString, rest: T.SingleOrLengthOpts): { head: T.PsString | undefined, rest: T.SingleOrLengthOpts } { - if ("long" in rest) { - return { - // whether or not to include the head (for irreg tlul) -- eww // TODO: make nicer? - head: removeBa(rest.long[0]).p.slice(0, head.p.length) === head.p - ? head : undefined, - rest: { - long: getHeadAndRest(head, rest.long).rest, - short: getHeadAndRest(head, rest.short).rest, - ...rest.mini ? { - mini: getHeadAndRest(head, rest.mini).rest, - } : {}, - }, - }; - } - let headMismatch = false; - const restM = rest.map((psRaw) => { - const ps = removeBa(psRaw); - const pMatches = removeAccents(ps.p.slice(0, head.p.length)) === head.p - const fMatches = removeAccents(ps.f.slice(0, head.f.length)) === removeAccents(head.f); - if (!(pMatches && fMatches)) { - headMismatch = true; - return psRaw; - // throw new Error(`split head does not match - ${JSON.stringify(ps)} ${JSON.stringify(head)}`); - } - return { - p: ps.p.slice(head.p.length), - f: ps.f.slice(head.f.length), - } - }); - return { - head: headMismatch ? undefined : head, - rest: restM, - } -} - -function getMatrixBlock(f: { - mascSing: T.SingleOrLengthOpts; - mascPlur: T.SingleOrLengthOpts; - femSing: T.SingleOrLengthOpts; - femPlur: T.SingleOrLengthOpts; -} | T.SingleOrLengthOpts, objectPerson: T.Person | undefined, kingPerson: T.Person): T.SingleOrLengthOpts { - // @ts-ignore - if (!("mascSing" in f)) { - return f; - } - function personToLabel(p: T.Person): "mascSing" | "mascPlur" | "femSing" | "femPlur" { - if (p === T.Person.FirstSingMale || p === T.Person.SecondSingMale || p === T.Person.ThirdSingMale) { - return "mascSing"; - } - if (p === T.Person.FirstSingFemale || p === T.Person.SecondSingFemale || p === T.Person.ThirdSingFemale) { - return "femSing"; - } - if (p === T.Person.FirstPlurMale || p === T.Person.SecondPlurMale || p === T.Person.ThirdPlurMale) { - return "mascPlur"; - } - return "femPlur"; - } - // if there's an object the matrix will agree with that, otherwise with the kingPerson (subject for intransitive) - const person = (objectPerson === undefined) ? kingPerson : objectPerson; - return f[personToLabel(person)]; -} - export function getKingAndServant(isPast: boolean, isTransitive: boolean): { king: "subject", servant: "object" } | { king: "object", servant: "subject" } | @@ -697,23 +291,6 @@ function isFirstOrSecondPersPronoun(o: "none" | T.NPSelection | T.Person.ThirdPl return [0,1,2,3,6,7,8,9].includes(o.selection.person); } -function isPerfective(t: T.Tense): boolean { - if (isPerfectTense(t)) return false; - if (t === "presentVerb" || t === "imperfectiveFuture" || t === "imperfectivePast" || t === "habitualImperfectivePast") { - return false; - } - if (t === "perfectiveFuture" || t === "subjunctiveVerb" || t === "perfectivePast" || t === "habitualPerfectivePast") { - return true; - } - if (t === "perfectiveFutureModal" || t === "subjunctiveVerbModal" || t === "perfectivePastModal" || t === "habitualPerfectivePastModal") { - return true; - } - if (t === "perfectiveImperative") { - return true; - } - return false; -} - function isMascSingAnimatePattern4(np: T.NPSelection): boolean { if (np.selection.type !== "noun") { return false; diff --git a/src/lib/src/verb-info.test.ts b/src/lib/src/verb-info.test.ts index a73fb0c..225271f 100644 --- a/src/lib/src/verb-info.test.ts +++ b/src/lib/src/verb-info.test.ts @@ -1298,7 +1298,7 @@ const toTest = [ entry: {"i":9369,"ts":1527822084,"p":"کار","g":"","f":"kaar","e":"work, job, business, stuff to do","c":"n. m."}, person: 4, }, - auxVerb: {"i":10058,"ts":1527812752,"p":"کول","g":"","f":"kawul","e":"to do (an action or activity)","c":"v. trans. irreg. dyn. aux.","ssp":"وکړ","ssf":"óokR","prp":"وکړل","prf":"óokRul","pprtp":"کړی","pprtf":"kúRey","diacExcept":true}, + auxVerb: {"ts":1527812752,"i":11033,"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","separationAtP":1,"separationAtF":2,"diacExcept":true,"ec":"do,does,doing,did,done"}, intransitiveForm: { entry: { entry: {"i":9371,"ts":1527812732,"p":"کار کېدل","g":"","f":"kaar kedul","e":"to work","l":1527822084,"c":"v. intrans. dyn. comp."}, @@ -1377,17 +1377,7 @@ const toTest = [ plural: { p: "منډې", f: "munDe" }, person: 11, }, - auxVerb: { - ts: 1527815399, - p: "وهل", - f: "wahul", - g:"", - e: "to hit", - c: "v. trans.", - i: 12183, - tppp: "واهه", - tppf: "waahu", - }, + auxVerb: {"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"}, singularForm: { entry: { entry: {"i":12101,"ts":1527812939,"p":"منډې وهل","g":"","f":"munDe wahul","e":"to run","l":1527815805,"c":"v. dyn. comp. trans. sing. or plur."}, @@ -1423,17 +1413,7 @@ const toTest = [ entry: {"i":12098,"ts":1527815805,"p":"منډه","g":"","f":"múnDa","e":"run, running","c":"n. f."}, person: 5, }, - auxVerb: { - ts: 1527815399, - p: "وهل", - f: "wahul", - g:"", - e: "to hit", - c: "v. trans.", - i: 12183, - tppp: "واهه", - tppf: "waahu", - }, + auxVerb: {"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"}, }, }, }, @@ -1537,7 +1517,7 @@ const toTest = [ entry: {"i":7909,"ts":1527819252,"p":"شروع","g":"","f":"shUróo'","e":"beginning, start, undertaking","c":"n. m."}, person: 4, }, - auxVerb: {"i":10058,"ts":1527812752,"p":"کول","g":"","f":"kawul","e":"to do (an action or activity)","c":"v. trans. irreg. dyn. aux.","ssp":"وکړ","ssf":"óokR","prp":"وکړل","prf":"óokRul","pprtp":"کړی","pprtf":"kúRey","diacExcept":true}, + auxVerb: {"ts":1527812752,"i":11033,"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","separationAtP":1,"separationAtF":2,"diacExcept":true,"ec":"do,does,doing,did,done"}, intransitiveForm: { entry: { entry: {"i":7910,"ts":1527819253,"p":"شروع کېدل","g":"","f":"shUróo' kedul","e":"to start, to begin","l":1527819252,"c":"v. intrans. dyn. comp."}, @@ -1680,7 +1660,7 @@ const toTest = [ entry: {"i":4769,"ts":1527813972,"p":"چیغه","g":"","f":"chéegha","e":"yell, scream, cry","c":"n. f."}, person: 5, }, - auxVerb: {"i":10058,"ts":1527812752,"p":"کول","g":"","f":"kawul","e":"to do (an action or activity)","c":"v. trans. irreg. dyn. aux.","ssp":"وکړ","ssf":"óokR","prp":"وکړل","prf":"óokRul","pprtp":"کړی","pprtf":"kúRey","diacExcept":true}, + auxVerb: {"ts":1527812752,"i":11033,"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","separationAtP":1,"separationAtF":2,"diacExcept":true,"ec":"do,does,doing,did,done"}, intransitiveForm: { entry: { entry: {"i":4770,"ts":1608137130992,"p":"چیغه کېدل","g":"","f":"chéegha kedul","e":"to yell, scream, cry out","l":1527813972,"c":"v. intrans. dyn. comp."}, @@ -1887,22 +1867,7 @@ const toTest = [ }, "person": 11 }, - "auxVerb": { - "i": 10058, - "ts": 1527812752, - "p": "کول", - "f": "kawul", - "g": "", - "e": "to do (an action or activity)", - "c": "v. trans. irreg. dyn. aux.", - "ssp": "وکړ", - "ssf": "óokR", - "prp": "وکړل", - "prf": "óokRul", - "pprtp": "کړی", - "pprtf": "kúRey", - "diacExcept": true - }, + "auxVerb": {"ts":1527812752,"i":11033,"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","separationAtP":1,"separationAtF":2,"diacExcept":true,"ec":"do,does,doing,did,done"}, "singularForm": { entry: { entry: {"i":4770,"ts":1608137130992,"p":"چیغې کول","g":"","f":"chéeghe kawul","e":"to yell, scream, cry out","l":1527813972,"c":"v. dyn. comp. trans."}, @@ -2014,22 +1979,7 @@ const toTest = [ }, "person": 5 }, - "auxVerb": { - "i": 10058, - "ts": 1527812752, - "p": "کول", - "f": "kawul", - "g": "", - "e": "to do (an action or activity)", - "c": "v. trans. irreg. dyn. aux.", - "ssp": "وکړ", - "ssf": "óokR", - "prp": "وکړل", - "prf": "óokRul", - "pprtp": "کړی", - "pprtf": "kúRey", - "diacExcept": true - }, + "auxVerb": {"ts":1527812752,"i":11033,"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","separationAtP":1,"separationAtF":2,"diacExcept":true,"ec":"do,does,doing,did,done"}, "intransitiveForm": { entry: { entry: {"i":4770,"ts":1608137130992,"p":"چیغې کېدل","g":"","f":"chéeghe kedul","e":"to yell, scream, cry out","l":1527813972,"c":"v. intrans. dyn. comp."}, diff --git a/src/types.ts b/src/types.ts index 20f6255..d9a921e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -6,6 +6,10 @@ * */ +type Prettify = { + [K in keyof T]: T[K]; +} & {}; + export type PsStringField = "p" | "f"; export type PsString = { [k in PsStringField]: string; @@ -558,7 +562,7 @@ export type UnisexAnimNounEntry = UnisexNounEntry & { __brand4: "an anim unisex export type AdverbEntry = DictionaryEntry & { c: string } & { __brand: "an adverb entry" }; export type LocativeAdverbEntry = AdverbEntry & { __brand2: "a locative adverb entry" }; export type AdjectiveEntry = DictionaryEntry & { c: string } & { __brand: "an adjective entry" }; -export type VerbDictionaryEntry = DictionaryEntry & { __brand: "a verb entry" }; +export type VerbDictionaryEntry = DictionaryEntry & { c: string } & { __brand: "a verb entry" }; export type VerbEntry = { entry: VerbDictionaryEntry, // TODO: the compliment could also be typed? Maybe? @@ -668,6 +672,22 @@ export type VerbSelectionComplete = Omit = { export type EquativeBlock = { type: "equative", equative: EquativeRendered }; -export type PerfectParticipleBlock = { - type: "perfectParticipleBlock", - ps: SingleOrLengthOpts, - verb: VerbRenderedBlock, - person: Person, - complementWelded: undefined | Rendered | Rendered, -}; -export type PerfectEquativeBlock = { - type: "perfectEquativeBlock", - ps: PsString[], - person: Person, -}; -export type ModalVerbBlock = { - type: "modalVerbBlock", - ps: SingleOrLengthOpts, - verb: VerbRenderedBlock, - complementWelded: undefined | Rendered | Rendered, -}; -export type ModalVerbKedulPart = { - type: "modalVerbKedulPart", - ps: PsString[], - verb: VerbRenderedBlock, -}; -export type PerfectiveHeadBlock = { type: "perfectiveHead", ps: PsString }; -export type VerbRenderedBlock = { - type: "verb", - block: Omit & { - hasBa: boolean, - ps: SingleOrLengthOpts, - person: Person, - complementWelded: undefined | Rendered | Rendered, - }, -}; - export type Block = { key: number, block: | Rendered @@ -1043,22 +1029,12 @@ export type Block = { | Rendered | Rendered | Rendered - | PerfectParticipleBlock - | PerfectEquativeBlock - | ModalVerbBlock - | ModalVerbKedulPart | { type: "negative", imperative: boolean } - | PerfectiveHeadBlock - | VerbRenderedBlock - | EquativeBlock; -} - -export type RenderedVerbB = VerbRenderedBlock - | PerfectiveHeadBlock - | ModalVerbBlock - | ModalVerbKedulPart - | PerfectEquativeBlock - | PerfectParticipleBlock; + | EquativeBlock + | VB + | VBE + | VHead, +}; export type Kid = { key: number, @@ -1124,7 +1100,7 @@ export type NComp = { comp: Comp, }; -// Complement can be one of +// element can be one of // - adjective // - locative adv // - sandwich (TODO)