From 5f8c4ba87647859a9c51b3aa6cf19cbb47d8385b Mon Sep 17 00:00:00 2001 From: adueck Date: Wed, 19 Jul 2023 16:12:49 +0400 Subject: [PATCH] more work, still need to get dynamic compounds working on new verb engine --- .vscode/settings.json | 5 + package.json | 2 + src/components/src/PersInfsPicker.tsx | 94 +- src/components/src/TableCell.tsx | 113 +- src/components/src/VerbFormDisplay.tsx | 238 +- src/components/src/verb-info/VerbInfo.tsx | 379 ++- .../src/vp-explorer/AllTensesDisplay.tsx | 134 +- .../src/vp-explorer/NewVerbFormDisplay.tsx | 255 ++ .../src/vp-explorer/VPChartDisplay.tsx | 57 +- .../src/vp-explorer/chart-builder.tsx | 157 + src/lib/src/fp-ps.ts | 43 + src/lib/src/misc-helpers.ts | 391 +-- .../src/new-verb-engine/render-verb.test.ts | 2662 ++++++++++------- src/lib/src/new-verb-engine/render-verb.ts | 615 ++-- .../new-verb-engine/roots-and-stems.test.ts | 1762 +++++++---- .../src/new-verb-engine/roots-and-stems.ts | 737 +++-- src/lib/src/phrase-building/compile.ts | 793 +++-- src/lib/src/phrase-building/render-vp.ts | 548 ++-- src/lib/src/phrase-building/vp-tools.ts | 609 ++-- src/lib/src/verb-info.ts | 2615 +++++++++------- src/types.ts | 1601 +++++----- yarn.lock | 10 + 22 files changed, 8343 insertions(+), 5477 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/components/src/vp-explorer/NewVerbFormDisplay.tsx create mode 100644 src/components/src/vp-explorer/chart-builder.tsx create mode 100644 src/lib/src/fp-ps.ts diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..d949d6d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "typescript.preferences.autoImportFileExcludePatterns": [ + "../../library.ts" + ], +} \ No newline at end of file diff --git a/package.json b/package.json index 3f860a5..059d31c 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,8 @@ ] }, "dependencies": { + "fp-ts": "^2.16.0", + "react-media-hook": "^0.5.0", "react-select": "^5.4.0" } } diff --git a/src/components/src/PersInfsPicker.tsx b/src/components/src/PersInfsPicker.tsx index 24c7321..34c1e9a 100644 --- a/src/components/src/PersInfsPicker.tsx +++ b/src/components/src/PersInfsPicker.tsx @@ -9,50 +9,64 @@ import * as T from "../../types"; const persInfs: { - label: string; - value: T.PersonInflectionsField; + label: string; + value: T.PersonInflectionsField; }[] = [ - { - label: "masc. sing.", - value: "mascSing", - }, - { - label: "fem. sing.", - value: "femSing", - }, - { - label: "masc. plur.", - value: "mascPlur", - }, - { - label: "fem. plur", - value: "femPlur", - } + { + label: "masc. sing.", + value: "mascSing", + }, + { + label: "fem. sing.", + value: "femSing", + }, + { + label: "masc. plur.", + value: "mascPlur", + }, + { + label: "fem. plur", + value: "femPlur", + }, ]; function PersInfsPicker(props: { - transitivity: T.Transitivity, - persInf: T.PersonInflectionsField, - handleChange: (persInf: T.PersonInflectionsField) => void, + subjOrObj: "subject" | "object"; + persInf: T.PersonInflectionsField; + handleChange: (persInf: T.PersonInflectionsField) => void; }) { - function hChange(e: any) { - const newValue = e.target.value as T.PersonInflectionsField; - props.handleChange(newValue); - } - return
-
- When the {props.transitivity === "intransitive" ? "subject" : "object"} is -
-
- -
-
; + function hChange(e: any) { + const newValue = e.target.value as T.PersonInflectionsField; + props.handleChange(newValue); + } + return ( +
+
+ When the {props.subjOrObj} is +
+
+ +
+
+ ); } -export default PersInfsPicker; \ No newline at end of file +export default PersInfsPicker; diff --git a/src/components/src/TableCell.tsx b/src/components/src/TableCell.tsx index f38bfce..88a4b46 100644 --- a/src/components/src/TableCell.tsx +++ b/src/components/src/TableCell.tsx @@ -3,50 +3,79 @@ import * as T from "../../types"; import Pashto from "./Pashto"; import Phonetics from "./Phonetics"; -const arrowDown = - -; +const arrowDown = ( + + + +); -function TableCell({ item, textOptions, center, noBorder }: { - item: T.ArrayOneOrMore, - textOptions: T.TextOptions, - center?: boolean, - noBorder?: boolean, +function TableCell({ + item, + textOptions, + center, + noBorder, +}: { + item: T.PsString[]; + textOptions: T.TextOptions; + center?: boolean; + noBorder?: boolean; }) { - const [version, setVersion] = useState(0); - useEffect(() => setVersion(0), [item]); - function advanceVersion() { - setVersion((version + 1) % item.length); - } - const w = item[version] || item[0]; - return ( - -
-
-
- {w} -
-
- {w} -
- {w.e && (Array.isArray(w.e) - ? w.e.map(e =>
{e}
) - :
{w.e}
)} + const [version, setVersion] = useState(0); + useEffect(() => setVersion(0), [item]); + function advanceVersion() { + setVersion((version + 1) % item.length); + } + const w = item[version] || item[0]; + return ( + +
+
+
+ {w} +
+
+ {w} +
+ {w.e && + (Array.isArray(w.e) ? ( + w.e.map((e) => ( +
+ {e}
- {item.length > 1 && - - } -
- - ); + )) + ) : ( +
{w.e}
+ ))} +
+ {item.length > 1 && ( + + )} +
+ + ); } -export default TableCell; \ No newline at end of file +export default TableCell; diff --git a/src/components/src/VerbFormDisplay.tsx b/src/components/src/VerbFormDisplay.tsx index abf5cfc..9e1e730 100644 --- a/src/components/src/VerbFormDisplay.tsx +++ b/src/components/src/VerbFormDisplay.tsx @@ -13,107 +13,157 @@ import SingleItemDisplay from "./SingleItemDisplay"; import ButtonSelect from "./ButtonSelect"; import VerbTable from "./VerbTable"; import { - getEnglishPersonInfo, - isSentenceForm, + getEnglishPersonInfo, + isSentenceForm, } from "../../lib/src/misc-helpers"; -import { - isAllOne, -} from "../../lib/src/p-text-helpers"; +import { isAllOne } from "../../lib/src/p-text-helpers"; import * as T from "../../types"; -function agreementInfo(info: T.NonComboVerbInfo, displayForm: T.DisplayForm): React.ReactNode { - if (!displayForm.past) { - return null; - } - const beginning = "Verb agrees with the "; - const agreesWith = (info.transitivity !== "intransitive" && displayForm.past && !displayForm.passive) - ? "object" - : "subject"; - const extraExplanation = (!displayForm.past) - ? "" - : (info.transitivity === "grammatically transitive") - ? " which is an unwritten 3rd pers. masc. object." - : (info.type === "generative stative compound" || info.type === "dynamic compound") - ? ` which is the complement ${info.objComplement.plural ? info.objComplement.plural.p : info.objComplement.entry.p} (${getEnglishPersonInfo(info.objComplement.person)})` - : "" - return <>Note: {beginning}{agreesWith}{extraExplanation} +function agreementInfo( + info: T.NonComboVerbInfo, + displayForm: T.DisplayForm +): React.ReactNode { + if (!displayForm.past) { + return null; + } + const beginning = "Verb agrees with the "; + const agreesWith = + info.transitivity !== "intransitive" && + displayForm.past && + !displayForm.passive + ? "object" + : "subject"; + const extraExplanation = !displayForm.past + ? "" + : info.transitivity === "grammatically transitive" + ? " which is an unwritten 3rd pers. masc. object." + : info.type === "generative stative compound" || + info.type === "dynamic compound" + ? ` which is the complement ${ + info.objComplement.plural + ? info.objComplement.plural.p + : info.objComplement.entry.p + } (${getEnglishPersonInfo(info.objComplement.person)})` + : ""; + return ( + <> + Note: {beginning} + {agreesWith} + {extraExplanation} + + ); } -function VerbFormDisplay({ displayForm, textOptions, info, showingFormInfo, english, shortDefault }: { - displayForm: T.DisplayForm | T.VerbForm | T.ImperativeForm, - english?: T.EnglishBlock | string, - textOptions: T.TextOptions, - showingFormInfo: boolean, - info?: T.NonComboVerbInfo, - shortDefault?: boolean, +function VerbFormDisplay({ + displayForm, + textOptions, + info, + showingFormInfo, + english, + shortDefault, +}: { + displayForm: T.DisplayForm | T.VerbForm | T.ImperativeForm; + english?: T.EnglishBlock | string; + textOptions: T.TextOptions; + showingFormInfo: boolean; + info?: T.NonComboVerbInfo; + shortDefault?: boolean; }) { - const defaultLength = shortDefault ? "short" : "long"; - const [persInf, setPersInf] = useState("mascSing"); - const [length, setLength] = useState(defaultLength); - const [showingExplanation, setShowingExplanation] = useState(false); - const block = "label" in displayForm ? displayForm.form : displayForm; - const chosenPersInf = "mascSing" in block - ? block[persInf] - : block; - const form = "long" in chosenPersInf - ? chosenPersInf[length] || chosenPersInf.short - : chosenPersInf; - useEffect(() => { - if (length === "mini" && !("mini" in chosenPersInf)) { - setLength(defaultLength); - } - // setPersInf("mascSing"); - // setShowingExplanation(false); - }, [block, length, chosenPersInf, defaultLength]); - // TODO: This could be handled better to avoid the react-hooks/exhaustive-deps warning ? - useEffect(() => { - setShowingExplanation(false); - }, [block]); - const hasVariations = (!("masc" in form)) && (!("p" in form)) && (!isSentenceForm(form)) && !isAllOne(form as T.VerbBlock | T.ImperativeBlock); - return <> - {(("label" in displayForm && info) && showingFormInfo) && <> - {(hasVariations || displayForm.past) &&

- {agreementInfo(info, displayForm)} -

} -
- Formula: {displayForm.formula} - -
- {showingExplanation &&
- {displayForm.explanation} -
} - } - {"long" in chosenPersInf && -
- setLength(p as T.Length)} - /> -
- } - {("mascSing" in block && info) && setPersInf(p)} - transitivity={info.transitivity} - />} - {"masc" in form ? - - : "p" in form ? - - : - - } + const defaultLength = shortDefault ? "short" : "long"; + const [persInf, setPersInf] = useState("mascSing"); + const [length, setLength] = useState(defaultLength); + const [showingExplanation, setShowingExplanation] = useState(false); + const block = "label" in displayForm ? displayForm.form : displayForm; + const chosenPersInf = "mascSing" in block ? block[persInf] : block; + const form = + "long" in chosenPersInf + ? chosenPersInf[length] || chosenPersInf.short + : chosenPersInf; + useEffect(() => { + if (length === "mini" && !("mini" in chosenPersInf)) { + setLength(defaultLength); + } + // setPersInf("mascSing"); + // setShowingExplanation(false); + }, [block, length, chosenPersInf, defaultLength]); + // TODO: This could be handled better to avoid the react-hooks/exhaustive-deps warning ? + useEffect(() => { + setShowingExplanation(false); + }, [block]); + const hasVariations = + !("masc" in form) && + !("p" in form) && + !isSentenceForm(form) && + !isAllOne(form as T.VerbBlock | T.ImperativeBlock); + return ( + <> + {"label" in displayForm && info && showingFormInfo && ( + <> + {(hasVariations || displayForm.past) && ( +

+ {agreementInfo(info, displayForm)} +

+ )} +
+ Formula: {displayForm.formula} + +
+ {showingExplanation && ( +
{displayForm.explanation}
+ )} + + )} + {"long" in chosenPersInf && ( +
+ setLength(p as T.Length)} + /> +
+ )} + {"mascSing" in block && info && ( + setPersInf(p)} + subjOrObj={info.transitivity === "transitive" ? "object" : "subject"} + /> + )} + {"masc" in form ? ( + + ) : "p" in form ? ( + + ) : ( + + )} + ); } export default VerbFormDisplay; diff --git a/src/components/src/verb-info/VerbInfo.tsx b/src/components/src/verb-info/VerbInfo.tsx index 2024958..a6f76ce 100644 --- a/src/components/src/verb-info/VerbInfo.tsx +++ b/src/components/src/verb-info/VerbInfo.tsx @@ -6,14 +6,11 @@ * */ +import { CSSProperties, useState } from "react"; import { - CSSProperties, - useState, -} from "react"; -import { - pickPersInf, - hasPersInfs, - noPersInfs, + pickPersInf, + hasPersInfs, + noPersInfs, } from "../../../lib/src/misc-helpers"; import VerbInfoItemDisplay from "./VerbInfoItemDisplay"; import PersonInfsPicker from "../PersInfsPicker"; @@ -26,56 +23,75 @@ import fadedTree from "./faded-tree.svg"; // import video from "../icons/camera-video-fill"; const indentR = { - paddingLeft: "1rem", + paddingLeft: "1rem", }; const highlight = { - background: "rgba(255, 227, 10, 0.6)", + background: "rgba(255, 227, 10, 0.6)", }; const title: CSSProperties = { - fontWeight: "bolder", - marginBottom: "0.5rem", - marginTop: "0.5rem", + fontWeight: "bolder", + marginBottom: "0.5rem", + marginTop: "0.5rem", }; -export function RootsAndStems({ textOptions, info, hidePastParticiple, highlighted, noTails }: { - textOptions: T.TextOptions, - info: T.NonComboVerbInfo | T.PassiveRootsAndStems | T.AbilityRootsAndStems, - hidePastParticiple?: boolean, - highlighted?: T.RootsOrStemsToHighlight, - noTails?: boolean, +export function RootsAndStems({ + textOptions, + info, + hidePastParticiple, + highlighted, + noTails, +}: { + textOptions: T.TextOptions; + info: T.NonComboVerbInfo | T.PassiveRootsAndStems | T.AbilityRootsAndStems; + hidePastParticiple?: boolean; + highlighted?: T.RootsOrStemsToHighlight; + noTails?: boolean; }) { - const hasPerfectiveSplit = ("perfectiveSplit" in info.root && "perfectiveSplit" in info.stem) - && !!(info.root.perfectiveSplit || info.stem.perfectiveSplit); - const showPersInf = hasPersInfs(info); - const [persInf, setPersInf] = useState("mascSing"); - const [split, setSplit] = useState(false); - const perfectiveStem = (info.stem.perfectiveSplit && split) - ? info.stem.perfectiveSplit - : info.stem.perfective; - const perfectiveRoot = (info.root.perfectiveSplit && split) - ? info.root.perfectiveSplit - : info.root.perfective; - const colClass = "col col-md-5 px-0 mb-1"; - const rowClass = "row justify-content-between"; - return ( -
- {showPersInf && setPersInf(p)} - transitivity={"transitivity" in info ? info.transitivity : "intransitive"} - />} -
- {/*
("mascSing"); + const [split, setSplit] = useState(false); + const perfectiveStem = + info.stem.perfectiveSplit && split + ? info.stem.perfectiveSplit + : info.stem.perfective; + const perfectiveRoot = + info.root.perfectiveSplit && split + ? info.root.perfectiveSplit + : info.root.perfective; + const colClass = "col col-md-5 px-0 mb-1"; + const rowClass = "row justify-content-between"; + return ( +
+ {showPersInf && ( + setPersInf(p)} + subjOrObj={ + "transitivity" in info && info.transitivity === "intransitive" + ? "subject" + : "object" + } + /> + )} +
+ {/*
{info.def} @@ -85,127 +101,158 @@ export function RootsAndStems({ textOptions, info, hidePastParticiple, highlight {info.subDef}
} */} -
-
-
- -
-
-
-
- -
- {hasPerfectiveSplit &&
- -
} -
-
-
-
-
-
-
Imperfective Stem
-
-
- -
-
-
-
-
Perfective Stem
-
-
- -
-
-
-
-
-
-
Imperfective Root
-
-
- -
-
-
-
-
-
Perfective Root
-
-
- -
-
-
-
- {!hidePastParticiple && "participle" in info &&
-
Past Participle
- -
} -
+
+
+
+
-
- ); -} - -function VerbInfo({ info, textOptions, showingStemsAndRoots, toggleShowingSar, highlightInRootsAndStems, hidePastParticiple, hideTypeInfo }: { - info: T.NonComboVerbInfo, - textOptions: T.TextOptions, - showingStemsAndRoots: boolean, - highlightInRootsAndStems?: T.RootsOrStemsToHighlight, - toggleShowingSar: () => void, - hidePastParticiple?: boolean, - hideTypeInfo?: boolean, -}) { - const inf = noPersInfs(info.root.imperfective).long; - return ( -
- {!hideTypeInfo && } - +
+
+ +
+ {hasPerfectiveSplit && ( +
+ +
+ )} +
+
+
+
+
- +
Imperfective Stem
+
+
+ - +
+
+
+
+
Perfective Stem
+
+
+ +
+
+
+
+
+
+
Imperfective Root
+
+
+ +
+
+
+
+
+
Perfective Root
+
+
+ +
+
+
+
+ {!hidePastParticiple && "participle" in info && ( +
+
Past Participle
+ +
+ )}
- ); +
+
+ ); } -export default VerbInfo; \ No newline at end of file +function VerbInfo({ + info, + textOptions, + showingStemsAndRoots, + toggleShowingSar, + highlightInRootsAndStems, + hidePastParticiple, + hideTypeInfo, +}: { + info: T.NonComboVerbInfo; + textOptions: T.TextOptions; + showingStemsAndRoots: boolean; + highlightInRootsAndStems?: T.RootsOrStemsToHighlight; + toggleShowingSar: () => void; + hidePastParticiple?: boolean; + hideTypeInfo?: boolean; +}) { + const inf = noPersInfs(info.root.imperfective).long; + return ( +
+ {!hideTypeInfo && } + + + +
+ ); +} + +export default VerbInfo; diff --git a/src/components/src/vp-explorer/AllTensesDisplay.tsx b/src/components/src/vp-explorer/AllTensesDisplay.tsx index 2a44780..0d17f5c 100644 --- a/src/components/src/vp-explorer/AllTensesDisplay.tsx +++ b/src/components/src/vp-explorer/AllTensesDisplay.tsx @@ -1,59 +1,101 @@ -import { abilityTenseOptions, imperativeTenseOptions, perfectTenseOptions, verbTenseOptions } from "./verbTenseOptions"; +import { + abilityTenseOptions, + imperativeTenseOptions, + perfectTenseOptions, + verbTenseOptions, +} from "./verbTenseOptions"; import ChartDisplay from "./VPChartDisplay"; import Hider from "../Hider"; import * as T from "../../../types"; import useStickyState from "../useStickyState"; -import { conjugateVerb } from "../../../lib/src/verb-conjugation"; +import { isImperativeTense } from "../../../lib/src/type-predicates"; +// import { conjugateVerb } from "../../../lib/src/verb-conjugation"; -function AllTensesDisplay({ VS, opts }: { VS: T.VerbSelection, opts: T.TextOptions }) { - const [showing, setShowing] = useStickyState([], "VPTensesShowing"); - const [showFormulas, setShowFormulas] = useStickyState(false, "showFormulasWithCharts"); - const adjustShowing = (v: string) => { - if (showing.includes(v)) { - setShowing(os => os.filter(x => x !== v)); - } else { - setShowing(os => [v, ...os]); - } +function AllTensesDisplay({ + VS, + opts, +}: { + VS: T.VerbSelection; + opts: T.TextOptions; +}) { + const [showing, setShowing] = useStickyState([], "VPTensesShowing"); + const [showFormulas, setShowFormulas] = useStickyState( + false, + "showFormulasWithCharts" + ); + const adjustShowing = (v: string) => { + if (showing.includes(v)) { + setShowing((os) => os.filter((x) => x !== v)); + } else { + setShowing((os) => [v, ...os]); } - const options = VS.tenseCategory === "basic" - ? verbTenseOptions - : VS.tenseCategory === "perfect" - ? perfectTenseOptions - : VS.tenseCategory === "modal" - ? abilityTenseOptions - : imperativeTenseOptions; - const rawConjugations = conjugateVerb(VS.verb.entry, VS.verb.complement); - const conjugations = ("stative" in rawConjugations) - ? rawConjugations[VS.isCompound === "stative" ? "stative" : "dynamic"] - : ("transitive" in rawConjugations) - ? rawConjugations[VS.transitivity === "grammatically transitive" ? "grammaticallyTransitive" : "transitive"] - : rawConjugations; - function getTense(baseTense: T.VerbTense | T.PerfectTense | T.ImperativeTense): T.VerbTense | T.PerfectTense | T.ImperativeTense | T.AbilityTense { - return VS.tenseCategory === "modal" ? `${baseTense}Modal` as T.AbilityTense : baseTense; - } - return
-
setShowFormulas(x => !x)}> - 🧪 {!showFormulas ? "Show" : "Hide"} Formulas -
- {options.map((tense) =>
+ }; + const options = + VS.tenseCategory === "basic" + ? verbTenseOptions + : VS.tenseCategory === "perfect" + ? perfectTenseOptions + : VS.tenseCategory === "modal" + ? abilityTenseOptions + : imperativeTenseOptions; + // const rawConjugations = conjugateVerb(VS.verb.entry, VS.verb.complement); + // const conjugations = + // "stative" in rawConjugations + // ? rawConjugations[VS.isCompound === "stative" ? "stative" : "dynamic"] + // : "transitive" in rawConjugations + // ? rawConjugations[ + // VS.transitivity === "grammatically transitive" + // ? "grammaticallyTransitive" + // : "transitive" + // ] + // : rawConjugations; + function getTense( + baseTense: T.VerbTense | T.PerfectTense | T.ImperativeTense + ): T.VerbTense | T.PerfectTense | T.ImperativeTense | T.AbilityTense { + return VS.tenseCategory === "modal" + ? (`${baseTense}Modal` as T.AbilityTense) + : baseTense; + } + return ( +
+
setShowFormulas((x) => !x)} + > + 🧪 {!showFormulas ? "Show" : "Hide"} Formulas +
+ {options.map((tense) => { + const t = getTense(tense.value); + return ( +
adjustShowing(tense.value)} - hLevel={5} + label={tense.label} + showing={showing.includes(tense.value)} + handleChange={() => adjustShowing(tense.value)} + hLevel={5} > - {showFormulas &&
- {tense.formula} -
} + {showFormulas && ( +
+ {tense.formula} +
+ )} + {showing && ( + )}
-
)} -
; +
+ ); + })} +
+ ); } -export default AllTensesDisplay; \ No newline at end of file +export default AllTensesDisplay; diff --git a/src/components/src/vp-explorer/NewVerbFormDisplay.tsx b/src/components/src/vp-explorer/NewVerbFormDisplay.tsx new file mode 100644 index 0000000..c26c989 --- /dev/null +++ b/src/components/src/vp-explorer/NewVerbFormDisplay.tsx @@ -0,0 +1,255 @@ +import { useEffect, useState } from "react"; +import ButtonSelect from "../ButtonSelect"; +import { combineIntoText } from "../../../lib/src/phrase-building/compile"; +import { insertNegative } from "../../../lib/src/phrase-building/render-vp"; +import * as T from "../../../types"; +import TableCell from "../TableCell"; +import { choosePersInf, getLength } from "../../../lib/src/p-text-helpers"; +import genderColors from "../gender-colors"; +import { eqPsStringWVars } from "../../../lib/src/fp-ps"; +import PersInfsPicker from "../PersInfsPicker"; +import { useMediaPredicate } from "react-media-hook"; + +export const roleIcon = { + king: , + servant: , +}; + +function VerbChartDisplay({ + chart, + opts, + shortDefault, + transitivity, + past, +}: { + chart: T.OptionalPersonInflections< + T.SingleOrLengthOpts + >; + opts: T.TextOptions; + shortDefault?: boolean; + transitivity: T.Transitivity; + past: boolean; +}) { + const [length, setLength] = useState( + shortDefault ? "short" : "long" + ); + const [persInf, setPersInf] = useState("mascSing"); + useEffect(() => { + setLength("long"); + }, [chart]); + const desktop = useMediaPredicate("(min-width: 600px)"); + const chartWPers = choosePersInf(chart, persInf); + const chartWLength = getLength(chartWPers, length); + + const x = chartWLength.map(renderVerbOutputToText(false)); + const verbBlock = + x.length === 12 + ? [ + // 1st pers + [ + [x[0], x[6]], + [x[1], x[7]], + ], + // 2nd pers + [ + [x[2], x[8]], + [x[3], x[9]], + ], + // 3rd pers + [ + [x[4], x[10]], + [x[5], x[11]], + ], + ] + : [ + // 2nd pers + [ + [x[0], x[2]], + [x[1], x[3]], + ], + ]; + return ( + <> +
+ {"mascSing" in chart ? ( + + ) : ( +
+ )} +
+
+
+ +
+
+ {"long" in chartWPers && ( + + )} +
+ {desktop &&
} +
+ + + + + + + + + + {verbBlock.map((personRow, i) => ( + + ))} + +
+ Pers. + SingularPlural
+ + ); +} + +function PersonRow({ + person, + item, + opts, +}: { + person: number; + item: T.PsString[][][]; + opts: T.TextOptions; +}) { + const [singM, singF] = [item[0][0], item[1][0]]; + const [plurM, plurF] = [item[0][1], item[1][1]]; + // just show one single, ungendered row if the gender doesn't make a difference + if ( + eqPsStringWVars.equals(singM, singF) && + eqPsStringWVars.equals(plurM, plurF) + ) { + return ( + + ); + } + return ( + <> + + + + ); +} + +function GenderedPersonRow({ + person, + line, + gender, + opts, +}: { + person: number; + line: T.PsString[][]; + gender: T.Gender | undefined; + opts: T.TextOptions; +}) { + const pers = ["1st", "2nd", "3rd"]; // arr.length > 1 ? ["1st", "2nd", "3rd"] : ["2nd"]; + const rowLabel = `${pers[person]}${ + gender ? (gender === "masc" ? " m." : " f.") : "" + }`; + const color = !gender ? "inherit" : genderColors[gender]; + return ( + + + {rowLabel} + + + + + ); +} + +function LengthSelection({ + value, + onChange, + hasMini, +}: { + hasMini: boolean; + value: T.Length; + onChange: (v: T.Length) => void; +}) { + return ( +
+ onChange(p as T.Length)} + /> +
+ ); +} + +function renderVerbOutputToText(negative: boolean) { + return function (v: T.RenderVerbOutput): T.PsString[] { + const blocks = insertNegative( + v.vbs, + negative, + false /* TODO: apply imperative */ + ); + return combineIntoText(blocks, 0 /* TODO: why is this needed */); + }; +} + +function AgreementInfo({ + transitivity, + past, +}: { + transitivity: T.Transitivity; + past: boolean; +}) { + return ( +
+ {roleIcon.king} agrees w/{" "} + + {transitivity !== "intransitive" && past ? "object" : "subject"} + +
+ ); +} + +export default VerbChartDisplay; diff --git a/src/components/src/vp-explorer/VPChartDisplay.tsx b/src/components/src/vp-explorer/VPChartDisplay.tsx index 46b5c6d..f93315d 100644 --- a/src/components/src/vp-explorer/VPChartDisplay.tsx +++ b/src/components/src/vp-explorer/VPChartDisplay.tsx @@ -1,24 +1,43 @@ -import { - getTenseVerbForm, -} from "../../../lib/src/phrase-building/vp-tools"; -import VerbFormDisplay from "../VerbFormDisplay"; +import NewVerbFormDisplay from "./NewVerbFormDisplay"; import * as T from "../../../types"; +import { buildVerbChart } from "./chart-builder"; +import { isPastTense } from "../../library"; -function ChartDisplay({ conjugations, tense, opts, voice }: { - conjugations: T.VerbConjugation, - tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense, - opts: T.TextOptions, - voice: T.VerbSelection["voice"], +function ChartDisplay({ + verb, + tense, + opts, + voice, + imperative, + negative, + transitivity, +}: { + verb: T.VerbEntry; + tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense; + opts: T.TextOptions; + voice: T.VerbSelection["voice"]; + imperative: boolean; + negative: boolean; + transitivity: T.Transitivity; }) { - const form = getTenseVerbForm(conjugations, tense, voice, "charts", false); - return
- -
; + const verbChart = buildVerbChart({ + verb, + tense, + voice, + negative, + transitivity, + imperative, + }); + return ( +
+ +
+ ); } -export default ChartDisplay; \ No newline at end of file +export default ChartDisplay; diff --git a/src/components/src/vp-explorer/chart-builder.tsx b/src/components/src/vp-explorer/chart-builder.tsx new file mode 100644 index 0000000..a6c17b6 --- /dev/null +++ b/src/components/src/vp-explorer/chart-builder.tsx @@ -0,0 +1,157 @@ +import { renderVerb } from "../../../lib/src/new-verb-engine/render-verb"; +import * as T from "../../../types"; +import { equals } from "rambda"; +import { isPastTense } from "../../library"; + +export function buildVerbChart({ + verb, + tense, + transitivity, + voice, + imperative, + negative, +}: { + verb: T.VerbEntry; + tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense; + transitivity: T.Transitivity; + imperative: boolean; + voice: T.VerbSelection["voice"]; + negative: boolean; +}): T.OptionalPersonInflections> { + const allPersons = imperative + ? [ + T.Person.SecondSingMale, + T.Person.SecondSingFemale, + T.Person.SecondPlurMale, + T.Person.SecondPlurFemale, + ] + : ([...Array(12).keys()] as T.Person[]); + const isPast = isPastTense(tense); + function conjugateAllPers( + p?: T.Person + ): T.SingleOrLengthOpts { + const ps = allPersons.map((person) => { + const { subject, object } = + transitivity === "intransitive" + ? { subject: person, object: undefined } + : transitivity === "transitive" && isPast + ? { object: person, subject: 0 } + : { subject: person, object: p ?? 0 }; + return renderVerb({ + verb, + tense, + subject, + object, + voice, + negative, + }); + }); + const hasLengths = vIsLength("long")(ps[0]); + const hasMini = vIsLength("mini")(ps[0]); + return pullOutLengths(hasLengths, hasMini, ps); + } + if (transitivity === "transitive" && !isPast) { + return conflateIfNoCompGenNumDiff({ + mascSing: conjugateAllPers(T.Person.FirstSingMale), + mascPlur: conjugateAllPers(T.Person.FirstPlurMale), + femSing: conjugateAllPers(T.Person.FirstSingFemale), + femPlur: conjugateAllPers(T.Person.FirstPlurFemale), + }); + } else { + return conjugateAllPers(); + } +} + +const vIsLength = + (length: T.Length) => + ({ vbs: [ph, [v]] }: T.RenderVerbOutput): boolean => { + // there are a number of parts of the verb that could be considered + // to be length variations + // but we will take the first main verb block as the point of length variation + // w reg verbs - wahúlm vs. wahúm + // (when welded, the right side of the block) + // w ability or perfect - kawúley shum vs. kawéy shum + function checkVBForLengths(v: T.VB): boolean { + if (v.type === "welded") { + return checkVBForLengths(v.right); + } + if (length === "mini" && "mini" in v.ps && v.ps.mini) { + return true; + } + return length in v.ps; + } + return checkVBForLengths(v); + }; + +function grabLength( + length: T.Length, + { hasBa, vbs: [ph, v] }: T.RenderVerbOutput +): T.RenderVerbOutput { + function grabVBLength(vb: V): V { + if (vb.type === "welded") { + return { + ...vb, + right: grabVBLength(vb.right) as T.VBBasic | T.VBGenNum, + }; + } + if (!(length in vb.ps)) { + return vb; + } + return { + ...vb, + // @ts-ignore + ps: vb.ps[length], + }; + } + if (v.length === 2) { + const [vb, vbe] = v; + return { + objComp: undefined, + hasBa, + vbs: [ph, [grabVBLength(vb), vbe]], + }; + } + return { + objComp: undefined, + hasBa, + vbs: [ph, [grabVBLength(v[0])]], + }; +} + +function pullOutLengths( + hasLengths: boolean, + hasMini: boolean, + ps: T.RenderVerbOutput[] +): T.SingleOrLengthOpts { + if (!hasLengths) { + return ps; + } + const wLengths: T.LengthOptions = { + long: [], + short: [], + }; + ps.forEach((x) => { + wLengths.long.push(grabLength("long", x)); + wLengths.short.push(grabLength("short", x)); + }); + if (hasMini) { + wLengths.mini = []; + ps.forEach((x) => { + // @ts-ignore + wLengths.mini.push(grabLength("mini", x)); + }); + } + return wLengths; +} + +function conflateIfNoCompGenNumDiff( + v: T.PersonInflections> +): T.OptionalPersonInflections> { + const toCheck = [ + "femSing", + "femPlur", + "mascPlur", + ] as T.PersonInflectionsField[]; + const diffExists = toCheck.some((f) => !equals(v[f], v.mascSing)); + return diffExists ? v : v.mascSing; +} diff --git a/src/lib/src/fp-ps.ts b/src/lib/src/fp-ps.ts new file mode 100644 index 0000000..f26caa7 --- /dev/null +++ b/src/lib/src/fp-ps.ts @@ -0,0 +1,43 @@ +import { Eq, struct } from "fp-ts/Eq"; +import { Semigroup } from "fp-ts/Semigroup"; +import { Monoid } from "fp-ts/Monoid"; +import * as S from "fp-ts/string"; +import * as T from "../../types"; + +export const eqPsString: Eq = struct({ + p: S.Eq, + f: S.Eq, +}); + +export const eqPsStringWVars: Eq = { + equals: (x, y) => { + return ( + x.length === y.length && x.every((a, i) => eqPsString.equals(a, y[i])) + ); + }, +}; + +export const semigroupPsStringWVars: Semigroup = { + concat: (x, y) => + x.flatMap((a) => y.map((b) => semigroupPsString.concat(a, b))), +}; + +export const semigroupPsString: Semigroup = { + concat: (x, y) => ({ + p: x.p + y.p, + f: x.f + y.f, + }), +}; + +export const monoidPsString: Monoid = { + concat: semigroupPsString.concat, + empty: { + p: "", + f: "", + }, +}; + +export const monoidPsStringWVars: Monoid = { + concat: semigroupPsStringWVars.concat, + empty: [monoidPsString.empty], +}; diff --git a/src/lib/src/misc-helpers.ts b/src/lib/src/misc-helpers.ts index 7e473b4..ab20bed 100644 --- a/src/lib/src/misc-helpers.ts +++ b/src/lib/src/misc-helpers.ts @@ -10,50 +10,51 @@ import * as T from "../../types"; import { fmapSingleOrLengthOpts } from "./fmaps"; export const blank: T.PsString = { - p: "_____", - f: "_____", + p: "_____", + f: "_____", }; export const kidsBlank: T.PsString = { p: "___", f: "___" }; /** * returns the main entry of a VerbEntry or just the entry of a DictionaryEntry - * + * * @param e FullEntry * @returns DictionaryEntry */ export function entryOfFull(e: T.FullEntry): T.DictionaryEntry { - return "entry" in e ? e.entry : e; + return "entry" in e ? e.entry : e; } // just for type safety -export function noPersInfs(s: T.OptionalPersonInflections): S { - if ("mascSing" in s) { - // this path shouldn't be used, just for type safety - return s.mascSing; - } - return s; +export function noPersInfs( + s: T.OptionalPersonInflections +): S { + if ("mascSing" in s) { + // this path shouldn't be used, just for type safety + return s.mascSing; + } + return s; } export function ensureNonComboVerbInfo(i: T.VerbInfo): T.NonComboVerbInfo { - return "stative" in i - ? i.stative - : "transitive" in i - ? i.transitive - : i; + return "stative" in i ? i.stative : "transitive" in i ? i.transitive : i; } -export function pickPersInf(s: T.OptionalPersonInflections, persInf: T.PersonInflectionsField): T { - // @ts-ignore - if ("mascSing" in s) { - return s[persInf]; - } - return s; +export function pickPersInf( + s: T.OptionalPersonInflections, + persInf: T.PersonInflectionsField +): T { + // @ts-ignore + if ("mascSing" in s) { + return s[persInf]; + } + return s; } export function getFirstSecThird(p: T.Person): 1 | 2 | 3 { - if ([0, 1, 6, 7].includes(p)) return 1; - if ([2, 3, 8, 9].includes(p)) return 2; - return 3; + if ([0, 1, 6, 7].includes(p)) return 1; + if ([2, 3, 8, 9].includes(p)) return 2; + return 3; } // export function pickPersInf( @@ -82,140 +83,169 @@ export function getFirstSecThird(p: T.Person): 1 | 2 | 3 { // return s; // } -export function hasPersInfs(info: T.NonComboVerbInfo | T.PassiveRootsAndStems | T.AbilityRootsAndStems): boolean { - if ("participle" in info) { - return ( - "mascSing" in info.root.perfective || - "mascSing" in info.stem.perfective || - ("present" in info.participle && "mascSing" in info.participle.present) || - "mascSing" in info.participle.past - ); - } +export function hasPersInfs( + info: T.NonComboVerbInfo | T.PassiveRootsAndStems | T.AbilityRootsAndStems +): boolean { + if ("participle" in info) { return ( - "mascSing" in info.root.perfective || - "mascSing" in info.stem.perfective + "mascSing" in info.root.perfective || + "mascSing" in info.stem.perfective || + ("present" in info.participle && "mascSing" in info.participle.present) || + "mascSing" in info.participle.past ); + } + return ( + "mascSing" in info.root.perfective || "mascSing" in info.stem.perfective + ); } // TODO: deprecated using new verb rendering thing export function chooseParticipleInflection( - pPartInfs: T.SingleOrLengthOpts | T.SingleOrLengthOpts, - person: T.Person, + pPartInfs: + | T.SingleOrLengthOpts + | T.SingleOrLengthOpts, + person: T.Person ): T.SingleOrLengthOpts { - if ("long" in pPartInfs) { - return { - short: chooseParticipleInflection(pPartInfs.short, person) as T.PsString, - long: chooseParticipleInflection(pPartInfs.long, person) as T.PsString, - }; - } - if ("masc" in pPartInfs) { - const gender = personGender(person); - const infNum = personIsPlural(person) ? 1 : 0; - return pPartInfs[gender][infNum][0]; - } - return pPartInfs; // already just one thing + if ("long" in pPartInfs) { + return { + short: chooseParticipleInflection(pPartInfs.short, person) as T.PsString, + long: chooseParticipleInflection(pPartInfs.long, person) as T.PsString, + }; + } + if ("masc" in pPartInfs) { + const gender = personGender(person); + const infNum = personIsPlural(person) ? 1 : 0; + return pPartInfs[gender][infNum][0]; + } + return pPartInfs; // already just one thing } -export function getPersonNumber(gender: T.Gender, number: T.NounNumber): T.Person { - const base = gender === "masc" ? 4 : 5; - return base + (number === "singular" ? 0 : 6); +export function getPersonNumber( + gender: T.Gender, + number: T.NounNumber +): T.Person { + const base = gender === "masc" ? 4 : 5; + return base + (number === "singular" ? 0 : 6); } export function personFromVerbBlockPos(pos: [number, number]): T.Person { - return pos[0] + (pos[1] === 1 ? 6 : 0); + return pos[0] + (pos[1] === 1 ? 6 : 0); } -export function getPersonInflectionsKey(person: T.Person): T.PersonInflectionsField { - return `${personGender(person)}${personIsPlural(person) ? "Plur" : "Sing"}` as T.PersonInflectionsField; +export function getPersonInflectionsKey( + person: T.Person +): T.PersonInflectionsField { + return `${personGender(person)}${ + personIsPlural(person) ? "Plur" : "Sing" + }` as T.PersonInflectionsField; } export function spaceInForm(form: T.FullForm): boolean { - if ("mascSing" in form) { - return spaceInForm(form.mascSing); - } - if ("long" in form) { - return spaceInForm(form.long); - } - return form.p.includes(" "); + if ("mascSing" in form) { + return spaceInForm(form.mascSing); + } + if ("long" in form) { + return spaceInForm(form.long); + } + return form.p.includes(" "); } -export function getPersonFromVerbForm(form: T.SingleOrLengthOpts, person: T.Person): T.SentenceForm { - return fmapSingleOrLengthOpts(x => { - const [row, col] = getVerbBlockPosFromPerson(person); - return x[row][col]; - }, form); +export function getPersonFromVerbForm( + form: T.SingleOrLengthOpts, + person: T.Person +): T.SentenceForm { + return fmapSingleOrLengthOpts((x) => { + const [row, col] = getVerbBlockPosFromPerson(person); + return x[row][col]; + }, form); } -export function getVerbBlockPosFromPerson(person: T.Person): [0 | 1 | 2 | 3 | 4 | 5, 0 | 1] { - const plural = personIsPlural(person) - const row = (plural ? (person - 6) : person) as 0 | 1 | 2 | 3 | 4 | 5; - const col = plural ? 1 : 0; - return [row, col]; +export function getVerbBlockPosFromPerson( + person: T.Person +): [0 | 1 | 2 | 3 | 4 | 5, 0 | 1] { + const plural = personIsPlural(person); + const row = (plural ? person - 6 : person) as 0 | 1 | 2 | 3 | 4 | 5; + const col = plural ? 1 : 0; + return [row, col]; } -export function getAuxTransitivity(trans: T.Transitivity): "transitive" | "intransitive" { - return trans === "intransitive" ? "intransitive" : "transitive"; +export function getAuxTransitivity( + trans: T.Transitivity +): "transitive" | "intransitive" { + return trans === "intransitive" ? "intransitive" : "transitive"; } export function personGender(person: T.Person): T.Gender { - return person % 2 === 0 ? "masc" : "fem"; + return person % 2 === 0 ? "masc" : "fem"; } export function personNumber(person: T.Person): T.NounNumber { - return personIsPlural(person) ? "plural" : "singular"; + return personIsPlural(person) ? "plural" : "singular"; } export function personIsPlural(person: T.Person): boolean { - return person > 5; + return person > 5; } -export function getEnglishPersonInfo(person: T.Person, version?: "short" | "long"): string { - const p = ([0,1,6,7].includes(person) - ? "1st" - : [2,3,8,9].includes(person) - ? "2nd" - : "3rd") + " pers."; - const number = personIsPlural(person) ? "plur" : "sing"; - const n = version === "short" - ? (number === "plur" ? "pl" : "sg") : number; - const gender = personGender(person); - const g = version === "short" - ? (gender === "masc" ? "m" : "f") - : gender; - return `${p} ${n}. ${g}.`; +export function getEnglishPersonInfo( + person: T.Person, + version?: "short" | "long" +): string { + const p = + ([0, 1, 6, 7].includes(person) + ? "1st" + : [2, 3, 8, 9].includes(person) + ? "2nd" + : "3rd") + " pers."; + const number = personIsPlural(person) ? "plur" : "sing"; + const n = version === "short" ? (number === "plur" ? "pl" : "sg") : number; + const gender = personGender(person); + const g = version === "short" ? (gender === "masc" ? "m" : "f") : gender; + return `${p} ${n}. ${g}.`; } -export function getEnglishGenNumInfo(gender: T.Gender, number: T.NounNumber): string { - return `${gender === "masc" ? "masc" : "fem"} ${number === "plural" ? "plur." : "sing."}`; +export function getEnglishGenNumInfo( + gender: T.Gender, + number: T.NounNumber +): string { + return `${gender === "masc" ? "masc" : "fem"} ${ + number === "plural" ? "plur." : "sing." + }`; } export function personToGenNum(p: T.Person): { - gender: T.Gender, - number: T.NounNumber, + gender: T.Gender; + number: T.NounNumber; } { - return { - gender: personGender(p), - number: personNumber(p), - }; + return { + gender: personGender(p), + number: personNumber(p), + }; } -export function getEnglishParticipleInflection(person: T.Person, version?: "short" | "long"): string { - const number = personIsPlural(person) ? "plural" : "singular"; - const n = version === "short" - ? (number === "plural" ? "plur." : "sing.") : number; - const gender = personGender(person); - const g = gender; - return `${g}. ${n}`; +export function getEnglishParticipleInflection( + person: T.Person, + version?: "short" | "long" +): string { + const number = personIsPlural(person) ? "plural" : "singular"; + const n = + version === "short" ? (number === "plural" ? "plur." : "sing.") : number; + const gender = personGender(person); + const g = gender; + return `${g}. ${n}`; } -export function randomNumber(minInclusive: number, maxExclusive: number): number { - return Math.floor(Math.random() * (maxExclusive - minInclusive) + minInclusive); +export function randomNumber( + minInclusive: number, + maxExclusive: number +): number { + return Math.floor( + Math.random() * (maxExclusive - minInclusive) + minInclusive + ); } -export function randFromArray(arr: M[]): M { - return arr[ - Math.floor(Math.random()*arr.length) - ]; +export function randFromArray(arr: Readonly): M { + return arr[Math.floor(Math.random() * arr.length)]; } export const isFirstPerson = (p: T.Person) => [0, 1, 6, 7].includes(p); @@ -223,87 +253,92 @@ export const isSecondPerson = (p: T.Person) => [2, 3, 8, 9].includes(p); export const isThirdPerson = (p: T.Person) => [4, 5, 10, 11].includes(p); export function incrementPerson(p: T.Person): T.Person { - return (p + 1) % 12; + return (p + 1) % 12; } export function isSentenceForm(f: any): boolean { - if ("long" in f) { - return isSentenceForm(f.long); - } - return Array.isArray(f) && "p" in f[0]; + if ("long" in f) { + return isSentenceForm(f.long); + } + return Array.isArray(f) && "p" in f[0]; } -export function isNounAdjOrVerb(entry: T.DictionaryEntry): "nounAdj" | "verb" | false { - if (!entry.c) { - return false; - } - if (entry.c.includes("adj.") || entry.c.includes("n. m.") || entry.c.includes("n. f.")) { - return "nounAdj"; - } - if (entry.c.slice(0, 3) === "v. ") { - return "verb"; - } +export function isNounAdjOrVerb( + entry: T.DictionaryEntry +): "nounAdj" | "verb" | false { + if (!entry.c) { return false; + } + if ( + entry.c.includes("adj.") || + entry.c.includes("n. m.") || + entry.c.includes("n. f.") + ) { + return "nounAdj"; + } + if (entry.c.slice(0, 3) === "v. ") { + return "verb"; + } + return false; } /** * takes the ec field from a dictionary entry and produces an array of an EnglishVerbConjugation * for use with the conjugations display for showing English translation sentences of various verb * forms and conjugations - * - * @param ec - * @returns + * + * @param ec + * @returns */ export function parseEc(ec: string): T.EnglishVerbConjugationEc { - function isVowel(s: string): boolean { - return ["a", "e", "i", "o", "u"].includes(s); + function isVowel(s: string): boolean { + return ["a", "e", "i", "o", "u"].includes(s); + } + function makeRegularConjugations(s: string): T.EnglishVerbConjugationEc { + if (s === "get") { + return ["get", "gets", "getting", "got", "gotten"]; } - function makeRegularConjugations(s: string): T.EnglishVerbConjugationEc { - if (s === "get") { - return ["get","gets","getting","got","gotten"]; - } - if (s === "become") { - return ["become","becomes","becoming","became","become"]; - } - if (s === "make") { - return ["make","makes","making","made","made"]; - } - if (s === "have") { - return ["have","has","having","had","had"]; - } - if (s === "be") { - return ["am","is","being","was","been"]; - } - if ((s.slice(-1) === "y") && !isVowel(s.slice(-2)[0])) { - const b = s.slice(0, -1); - return [`${s}`, `${b}ies`, `${s}ing`, `${b}ied`, `${b}ied`]; - } - if (s.slice(-2) === "ss") { - return [`${s}`, `${s}es`, `${s}ing`, `${s}ed`, `${s}ed`]; - } - if (s.slice(-2) === "ie" && !isVowel(s.slice(-3)[0])) { - const b = s.slice(0, -2); - return [`${s}`, `${s}s`, `${b}ying`, `${s}d`, `${s}d`]; - } - const b = s === "" - ? "VERB" - : (s.slice(-1) === "e") - ? s.slice(0, -1) - : s; - return [`${s}`, `${s}s`, `${b}ing`, `${b}ed`, `${b}ed`]; + if (s === "become") { + return ["become", "becomes", "becoming", "became", "become"]; } - const items = ec.split(",").map(x => x.trim()); - return (items.length === 4) - ? [items[0], items[1], items[2], items[3], items[3]] - : (items.length === 5) - ? [items[0], items[1], items[2], items[3], items[4]] - : makeRegularConjugations(items[0]); + if (s === "make") { + return ["make", "makes", "making", "made", "made"]; + } + if (s === "have") { + return ["have", "has", "having", "had", "had"]; + } + if (s === "be") { + return ["am", "is", "being", "was", "been"]; + } + if (s.slice(-1) === "y" && !isVowel(s.slice(-2)[0])) { + const b = s.slice(0, -1); + return [`${s}`, `${b}ies`, `${s}ing`, `${b}ied`, `${b}ied`]; + } + if (s.slice(-2) === "ss") { + return [`${s}`, `${s}es`, `${s}ing`, `${s}ed`, `${s}ed`]; + } + if (s.slice(-2) === "ie" && !isVowel(s.slice(-3)[0])) { + const b = s.slice(0, -2); + return [`${s}`, `${s}s`, `${b}ying`, `${s}d`, `${s}d`]; + } + const b = s === "" ? "VERB" : s.slice(-1) === "e" ? s.slice(0, -1) : s; + return [`${s}`, `${s}s`, `${b}ing`, `${b}ed`, `${b}ed`]; + } + const items = ec.split(",").map((x) => x.trim()); + return items.length === 4 + ? [items[0], items[1], items[2], items[3], items[3]] + : items.length === 5 + ? [items[0], items[1], items[2], items[3], items[4]] + : makeRegularConjugations(items[0]); } -export function chooseLength(x: T.SingleOrLengthOpts, length: "long" | "short"): N { - // @ts-ignore - if ("long" in x) { - return x[length]; - } - return x; -} \ No newline at end of file +export function chooseLength( + x: T.SingleOrLengthOpts, + length: "long" | "short" +): N { + // @ts-ignore + if ("long" in x) { + return x[length]; + } + return x; +} 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 c07109e..229ee7b 100644 --- a/src/lib/src/new-verb-engine/render-verb.test.ts +++ b/src/lib/src/new-verb-engine/render-verb.test.ts @@ -3,1070 +3,1664 @@ import { vEntry } from "./rs-helpers"; import * as T from "../../../types"; import { negate } from "rambda"; import { personToGenNum } from "../misc-helpers"; +import { getTransitivity } from "../verb-info"; -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"}); -const achawul = vEntry({"ts":1527811872,"i":224,"p":"اچول","f":"achawul","g":"achawul","e":"to put, pour, drop, throw, put on","r":4,"c":"v. trans.","ec":"put,puts,putting,put,put"}); -const ganul = vEntry({"ts":1527812000,"i":11398,"p":"ګڼل","f":"gaNul, guNul","g":"gaNul,guNul","e":"to count, consider, reckon, suppose, assume","r":4,"c":"v. trans.","tppp":"ګاڼه","tppf":"gaaNu","ec":"deem"}); -const kawulStat = vEntry({"ts":1579015359582,"i":11030,"p":"کول","f":"kawul","g":"kawul","e":"to make ____ ____ (as in \"He's making me angry.\")","r":4,"c":"v. trans.","ssp":"کړ","ssf":"kR","prp":"کړل","prf":"kRul","pprtp":"کړی","pprtf":"kúRey","noOo":true,"ec":"make,makes,making,made,made"}); -const kawulDyn = vEntry({"ts":1527812752,"i":11031,"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 kedulStat = vEntry({"ts":1581086654898,"i":11100,"p":"کېدل","f":"kedul","g":"kedul","e":"to become _____","r":2,"c":"v. intrans.","ssp":"ش","ssf":"sh","prp":"شول","prf":"shwul","pprtp":"شوی","pprtf":"shúwey","noOo":true,"ec":"become"}); -const kedulDyn = vEntry({"ts":1527812754,"i":11101,"p":"کېدل","f":"kedul","g":"kedul","e":"to happen, occur","r":2,"c":"v. intrans.","ssp":"وش","ssf":"óosh","prp":"وشول","prf":"óoshwul","pprtp":"شوی","pprtf":"shúwey","diacExcept":true,"ec":"happen","separationAtP":1,"separationAtF":2}); -const raatlul = vEntry({"ts":1527815216,"i":6875,"p":"راتلل","f":"raatlúl","g":"raatlul","e":"to come","r":4,"c":"v. intrans.","psp":"راځ","psf":"raadz","ssp":"راش","ssf":"ráash","prp":"راغلل","prf":"ráaghlul","pprtp":"راغلی","pprtf":"raaghúley","tppp":"راغی","tppf":"ráaghey","noOo":true,"separationAtP":2,"separationAtF":3,"ec":"come,comes,coming,came,come"}); -const wartlul = vEntry({"ts":1585228579997,"i":14821,"p":"ورتلل","f":"wărtlul","g":"wartlul","e":"to come / go over to (third person or place)","r":4,"c":"v. intrans.","psp":"ورځ","psf":"wărdz","ssp":"ورش","ssf":"wársh","prp":"ورغلل","prf":"wárghlul","pprtp":"ورغلی","pprtf":"wărghúley","tppp":"ورغی","tppf":"wărghey","noOo":true,"separationAtP":2,"separationAtF":3,"ec":"come,comes,coming,came,come"}); -const osedul = vEntry({"ts":1527815139,"i":1127,"p":"اوسېدل","f":"osedul","g":"osedul","e":"to live, reside, stay, be","r":4,"c":"v. intrans.","shortIntrans":true,"diacExcept":true}); -const tlul = vEntry({"ts":1527815348,"i":3791,"p":"تلل","f":"tlul","g":"tlul","e":"to go","r":4,"c":"v. intrans.","psp":"ځ","psf":"dz","ssp":"لاړ ش","ssf":"láaR sh","prp":"لاړ","prf":"láaR","ec":"go,goes,going,went,gone"}); -const awuxtul = vEntry({"ts":1527814012,"i":1133,"p":"اوښتل","f":"awUxtul","g":"awUxtul","e":"to pass over, overturn, be flipped over, spill over, shift, change, diverge, pass, cross, abandon; to be sprained","r":4,"c":"v. intrans.","psp":"اوړ","psf":"awR","ec":"pass","ep":"over"}); -const khorul = vEntry({"ts":1527812790,"i":6002,"p":"خوړل","f":"khoRul","g":"khoRul","e":"to eat, to bite","r":4,"c":"v. trans.","psp":"خور","psf":"khor","tppp":"خوړ","tppf":"khoR","ec":"eat,eats,eating,ate,eaten"}); -const azmoyul = vEntry({"ts":1527811605,"i":468,"p":"ازمویل","f":"azmoyul","g":"azmoyul","e":"to attempt, try; to experiment, test","r":4,"c":"v. trans.","sepOo":true,"ec":"try"}); -const khatul = vEntry({"ts":1527814025,"i":5677,"p":"ختل","f":"khatul","g":"khatul","e":"to climb, ascend, rise, go up; to fall out, to fall off, to leave/dissapear; to turn out to be ...; to give a sentence (in law)","r":3,"c":"v. intrans.","psp":"خېژ","psf":"khejz","tppp":"خوت","tppf":"khot","ec":"climb"}); -const rasedul = vEntry({"ts":1527813573,"i":7057,"p":"رسېدل","f":"rasedul","g":"rasedul","e":"arrive, reach; (fig.) understand, attain to; mature, ripen","r":4,"c":"v. intrans.","shortIntrans":true,"ec":"arrive"}); -const weshul = vEntry({"ts":1527811701,"i":15106,"p":"وېشل","f":"weshul","g":"weshul","e":"divide, distribute, share","r":4,"c":"v. trans.","ec":"divide"}); -const watul = vEntry({"ts":1527823376,"i":14759,"p":"وتل","f":"watul","g":"watul","e":"to go out, exit, leave, emerge","r":4,"c":"v. intrans.","psp":"وځ","psf":"oodz","tppp":"واته","tppf":"waatu","ec":"go,goes,going,went,gone","ep":"out"}); -const wurul = vEntry({"ts":1527816865,"i":14903,"p":"وړل","f":"wuRúl, oRúl, wRul","g":"wuRul,oRul,wRul","e":"to take, carry, bear, move (inanimate objects); to win, earn (subjunctive یوسي - yósee or ویسي - wéesee, simple past یو یې وړلو - yo ye wRulo)","r":3,"c":"v. trans.","ssp":"یوس","ssf":"yos","prp":"یوړل","prf":"yóRul","tppp":"یوړ","tppf":"yoR","noOo":true,"separationAtP":2,"separationAtF":2,"diacExcept":true,"ec":"take,takes,taking,took,taken"}); -const kexodul = vEntry({"ts":1527812284,"i":11113,"p":"کېښودل","f":"kexodul","g":"kexodul","e":"to put, to put down, to set in place","r":4,"c":"v. trans.","psp":"ږد","psf":"Gd","ssp":"کېږد","ssf":"kéGd","noOo":true,"separationAtP":2,"separationAtF":2,"ec":"put,puts,putting,put,put"}); -const kenaastul = vEntry({"ts":1527812759,"i":11124,"p":"کېناستل","f":"kenaastul","g":"kenaastul","e":"to sit down, to have a seat","r":4,"c":"v. intrans.","psp":"کېن","psf":"ken","noOo":true,"separationAtP":2,"separationAtF":2,"ec":"sit,sits,sitting,sat","ep":"down"}); -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 prexodul = vEntry({"ts":1527815190,"i":2495,"p":"پرېښودل","f":"prexodúl","g":"prexodul","e":"to leave, abandon, forsake, let go, allow","r":4,"c":"v. trans.","psp":"پرېږد","psf":"preGd","noOo":true,"separationAtP":3,"separationAtF":3,"ec":"abandon"}); -const raawustul = vEntry({"ts":1527819827,"i":6955,"p":"راوستل","f":"raawustúl","g":"raawustul","e":"to bring, deliver (animate objects), obtain, extract","r":3,"c":"v. trans.","psp":"راول","psf":"raawul","noOo":true,"separationAtP":2,"separationAtF":3,"ec":"bring,brings,bringing,brought,brought"}); -const leedul = vEntry({"ts":1527812275,"i":12049,"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 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", +}); +const achawul = vEntry({ + ts: 1527811872, + i: 224, + p: "اچول", + f: "achawul", + g: "achawul", + e: "to put, pour, drop, throw, put on", + r: 4, + c: "v. trans.", + ec: "put,puts,putting,put,put", +}); +const ganul = vEntry({ + ts: 1527812000, + i: 11398, + p: "ګڼل", + f: "gaNul, guNul", + g: "gaNul,guNul", + e: "to count, consider, reckon, suppose, assume", + r: 4, + c: "v. trans.", + tppp: "ګاڼه", + tppf: "gaaNu", + ec: "deem", +}); +const kawulStat = vEntry({ + ts: 1579015359582, + i: 11030, + p: "کول", + f: "kawul", + g: "kawul", + e: 'to make ____ ____ (as in "He\'s making me angry.")', + r: 4, + c: "v. trans.", + ssp: "کړ", + ssf: "kR", + prp: "کړل", + prf: "kRul", + pprtp: "کړی", + pprtf: "kúRey", + noOo: true, + ec: "make,makes,making,made,made", +}); +const kawulDyn = vEntry({ + ts: 1527812752, + i: 11031, + 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 kedulStat = vEntry({ + ts: 1581086654898, + i: 11100, + p: "کېدل", + f: "kedul", + g: "kedul", + e: "to become _____", + r: 2, + c: "v. intrans.", + ssp: "ش", + ssf: "sh", + prp: "شول", + prf: "shwul", + pprtp: "شوی", + pprtf: "shúwey", + noOo: true, + ec: "become", +}); +const kedulDyn = vEntry({ + ts: 1527812754, + i: 11101, + p: "کېدل", + f: "kedul", + g: "kedul", + e: "to happen, occur", + r: 2, + c: "v. intrans.", + ssp: "وش", + ssf: "óosh", + prp: "وشول", + prf: "óoshwul", + pprtp: "شوی", + pprtf: "shúwey", + diacExcept: true, + ec: "happen", + separationAtP: 1, + separationAtF: 2, +}); +const raatlul = vEntry({ + ts: 1527815216, + i: 6875, + p: "راتلل", + f: "raatlúl", + g: "raatlul", + e: "to come", + r: 4, + c: "v. intrans.", + psp: "راځ", + psf: "raadz", + ssp: "راش", + ssf: "ráash", + prp: "راغلل", + prf: "ráaghlul", + pprtp: "راغلی", + pprtf: "raaghúley", + tppp: "راغی", + tppf: "ráaghey", + noOo: true, + separationAtP: 2, + separationAtF: 3, + ec: "come,comes,coming,came,come", +}); +const wartlul = vEntry({ + ts: 1585228579997, + i: 14821, + p: "ورتلل", + f: "wărtlul", + g: "wartlul", + e: "to come / go over to (third person or place)", + r: 4, + c: "v. intrans.", + psp: "ورځ", + psf: "wărdz", + ssp: "ورش", + ssf: "wársh", + prp: "ورغلل", + prf: "wárghlul", + pprtp: "ورغلی", + pprtf: "wărghúley", + tppp: "ورغی", + tppf: "wărghey", + noOo: true, + separationAtP: 2, + separationAtF: 3, + ec: "come,comes,coming,came,come", +}); +const osedul = vEntry({ + ts: 1527815139, + i: 1127, + p: "اوسېدل", + f: "osedul", + g: "osedul", + e: "to live, reside, stay, be", + r: 4, + c: "v. intrans.", + shortIntrans: true, + diacExcept: true, +}); +const tlul = vEntry({ + ts: 1527815348, + i: 3791, + p: "تلل", + f: "tlul", + g: "tlul", + e: "to go", + r: 4, + c: "v. intrans.", + psp: "ځ", + psf: "dz", + ssp: "لاړ ش", + ssf: "láaR sh", + prp: "لاړ", + prf: "láaR", + ec: "go,goes,going,went,gone", +}); +const awuxtul = vEntry({ + ts: 1527814012, + i: 1133, + p: "اوښتل", + f: "awUxtul", + g: "awUxtul", + e: "to pass over, overturn, be flipped over, spill over, shift, change, diverge, pass, cross, abandon; to be sprained", + r: 4, + c: "v. intrans.", + psp: "اوړ", + psf: "awR", + ec: "pass", + ep: "over", +}); +const khorul = vEntry({ + ts: 1527812790, + i: 6002, + p: "خوړل", + f: "khoRul", + g: "khoRul", + e: "to eat, to bite", + r: 4, + c: "v. trans.", + psp: "خور", + psf: "khor", + tppp: "خوړ", + tppf: "khoR", + ec: "eat,eats,eating,ate,eaten", +}); +const azmoyul = vEntry({ + ts: 1527811605, + i: 468, + p: "ازمویل", + f: "azmoyul", + g: "azmoyul", + e: "to attempt, try; to experiment, test", + r: 4, + c: "v. trans.", + sepOo: true, + ec: "try", +}); +const khatul = vEntry({ + ts: 1527814025, + i: 5677, + p: "ختل", + f: "khatul", + g: "khatul", + e: "to climb, ascend, rise, go up; to fall out, to fall off, to leave/dissapear; to turn out to be ...; to give a sentence (in law)", + r: 3, + c: "v. intrans.", + psp: "خېژ", + psf: "khejz", + tppp: "خوت", + tppf: "khot", + ec: "climb", +}); +const rasedul = vEntry({ + ts: 1527813573, + i: 7057, + p: "رسېدل", + f: "rasedul", + g: "rasedul", + e: "arrive, reach; (fig.) understand, attain to; mature, ripen", + r: 4, + c: "v. intrans.", + shortIntrans: true, + ec: "arrive", +}); +const weshul = vEntry({ + ts: 1527811701, + i: 15106, + p: "وېشل", + f: "weshul", + g: "weshul", + e: "divide, distribute, share", + r: 4, + c: "v. trans.", + ec: "divide", +}); +const watul = vEntry({ + ts: 1527823376, + i: 14759, + p: "وتل", + f: "watul", + g: "watul", + e: "to go out, exit, leave, emerge", + r: 4, + c: "v. intrans.", + psp: "وځ", + psf: "oodz", + tppp: "واته", + tppf: "waatu", + ec: "go,goes,going,went,gone", + ep: "out", +}); +const wurul = vEntry({ + ts: 1527816865, + i: 14903, + p: "وړل", + f: "wuRúl, oRúl, wRul", + g: "wuRul,oRul,wRul", + e: "to take, carry, bear, move (inanimate objects); to win, earn (subjunctive یوسي - yósee or ویسي - wéesee, simple past یو یې وړلو - yo ye wRulo)", + r: 3, + c: "v. trans.", + ssp: "یوس", + ssf: "yos", + prp: "یوړل", + prf: "yóRul", + tppp: "یوړ", + tppf: "yoR", + noOo: true, + separationAtP: 2, + separationAtF: 2, + diacExcept: true, + ec: "take,takes,taking,took,taken", +}); +const kexodul = vEntry({ + ts: 1527812284, + i: 11113, + p: "کېښودل", + f: "kexodul", + g: "kexodul", + e: "to put, to put down, to set in place", + r: 4, + c: "v. trans.", + psp: "ږد", + psf: "Gd", + ssp: "کېږد", + ssf: "kéGd", + noOo: true, + separationAtP: 2, + separationAtF: 2, + ec: "put,puts,putting,put,put", +}); +const kenaastul = vEntry({ + ts: 1527812759, + i: 11124, + p: "کېناستل", + f: "kenaastul", + g: "kenaastul", + e: "to sit down, to have a seat", + r: 4, + c: "v. intrans.", + psp: "کېن", + psf: "ken", + noOo: true, + separationAtP: 2, + separationAtF: 2, + ec: "sit,sits,sitting,sat", + ep: "down", +}); +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 prexodul = vEntry({ + ts: 1527815190, + i: 2495, + p: "پرېښودل", + f: "prexodúl", + g: "prexodul", + e: "to leave, abandon, forsake, let go, allow", + r: 4, + c: "v. trans.", + psp: "پرېږد", + psf: "preGd", + noOo: true, + separationAtP: 3, + separationAtF: 3, + ec: "abandon", +}); +const raawustul = vEntry({ + ts: 1527819827, + i: 6955, + p: "راوستل", + f: "raawustúl", + g: "raawustul", + e: "to bring, deliver (animate objects), obtain, extract", + r: 3, + c: "v. trans.", + psp: "راول", + psf: "raawul", + noOo: true, + separationAtP: 2, + separationAtF: 3, + ec: "bring,brings,bringing,brought,brought", +}); +const leedul = vEntry({ + ts: 1527812275, + i: 12049, + 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 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."}, + { + 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."}, + { + 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 stureyKawul = vEntry( - {"ts":1591033078746,"i":7877,"p":"ستړی کول","f":"stuRey kawul","g":"stuReykawul","e":"to make tired, wear out","r":4,"c":"v. stat. comp. trans.","l":1527815306,"ec":"make","ep":"tired"}, - {"ts":1527815306,"i":7876,"p":"ستړی","f":"stúRey","g":"stuRey","e":"tired","r":4,"c":"adj. / adv."}, + { + ts: 1591033078746, + i: 7877, + p: "ستړی کول", + f: "stuRey kawul", + g: "stuReykawul", + e: "to make tired, wear out", + r: 4, + c: "v. stat. comp. trans.", + l: 1527815306, + ec: "make", + ep: "tired", + }, + { + ts: 1527815306, + i: 7876, + p: "ستړی", + f: "stúRey", + g: "stuRey", + e: "tired", + r: 4, + c: "adj. / adv.", + } ); const stureyKedul = vEntry( - {"ts":1591033069786,"i":7878,"p":"ستړی کېدل","f":"stuRey kedul","g":"stuReykedul","e":"to get tired, fatigued","r":4,"c":"v. stat. comp. intrans.","l":1527815306,"ec":"get","ep":"tired"}, - {"ts":1527815306,"i":7876,"p":"ستړی","f":"stúRey","g":"stuRey","e":"tired","r":4,"c":"adj. / adv."}, + { + ts: 1591033069786, + i: 7878, + p: "ستړی کېدل", + f: "stuRey kedul", + g: "stuReykedul", + e: "to get tired, fatigued", + r: 4, + c: "v. stat. comp. intrans.", + l: 1527815306, + ec: "get", + ep: "tired", + }, + { + ts: 1527815306, + i: 7876, + p: "ستړی", + f: "stúRey", + g: "stuRey", + e: "tired", + r: 4, + c: "adj. / adv.", + } ); -const ooPh: T.PH = { type: "PH", ps: { p: "و", f: "óo" }}; +const ooPh: T.PH = { type: "PH", ps: { p: "و", f: "óo" } }; test("basic tenses", () => { - expect(renderVerb({ - verb: wahul, - tense: "presentVerb", - person: T.Person.FirstSingMale, - complementGenNum: personToGenNum(T.Person.FirstSingMale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: false, - - vbs: [ - [], - [ - { type: "VB", ps: [{ p: "وهم", f: "wahum" }], person: T.Person.FirstSingMale }, - ], - ], - }); - expect(renderVerb({ - verb: wahul, - tense: "subjunctiveVerb", - person: T.Person.SecondSingMale, - complementGenNum: personToGenNum(T.Person.SecondSingMale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: false, - vbs: [ - [{ type: "PH", ps: { f: "óo", p: "و" }}], - [ - { type: "VB", ps: [{ p: "وهې", f: "wahe" }], person: T.Person.SecondSingMale }, - ], - ], - }); - expect(renderVerb({ - verb: wahul, - tense: "habitualPerfectivePast", - person: T.Person.ThirdSingFemale, - complementGenNum: personToGenNum(T.Person.ThirdSingFemale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: true, - vbs: [ - [{ type: "PH", ps: { f: "óo", p: "و" }}], - [ - { - type: "VB", - ps: { - long: [{ p: "وهله", f: "wahula" }], - short: [{ p: "وهه", f: "waha" }], - }, - person: T.Person.ThirdSingFemale - }, - ], - ], - }); + expect( + renderVerb({ + verb: wahul, + tense: "presentVerb", + subject: T.Person.FirstSingMale, + object: T.Person.ThirdSingMale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + + vbs: [ + [], + [ + { + type: "VB", + ps: [{ p: "وهم", f: "wahum" }], + person: T.Person.FirstSingMale, + }, + ], + ], + }); + expect( + renderVerb({ + verb: wahul, + tense: "subjunctiveVerb", + subject: T.Person.SecondSingMale, + object: T.Person.ThirdPlurMale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + vbs: [ + [{ type: "PH", ps: { f: "óo", p: "و" } }], + [ + { + type: "VB", + ps: [{ p: "وهې", f: "wahe" }], + person: T.Person.SecondSingMale, + }, + ], + ], + }); + expect( + renderVerb({ + verb: wahul, + tense: "habitualPerfectivePast", + subject: T.Person.ThirdSingMale, + object: T.Person.ThirdSingFemale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: true, + vbs: [ + [{ type: "PH", ps: { f: "óo", p: "و" } }], + [ + { + type: "VB", + ps: { + long: [{ p: "وهله", f: "wahula" }], + short: [{ p: "وهه", f: "waha" }], + }, + person: T.Person.ThirdSingFemale, + }, + ], + ], + }); + expect( + renderVerb({ + verb: wahul, + tense: "presentVerb", + subject: T.Person.FirstSingMale, + object: T.Person.ThirdSingMale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + + vbs: [ + [], + [ + { + type: "VB", + ps: [{ p: "وهم", f: "wahum" }], + person: T.Person.FirstSingMale, + }, + ], + ], + }); + expect( + renderVerb({ + verb: wahul, + tense: "subjunctiveVerb", + subject: T.Person.SecondSingMale, + object: T.Person.ThirdSingMale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + vbs: [ + [{ type: "PH", ps: { f: "óo", p: "و" } }], + [ + { + type: "VB", + ps: [{ p: "وهې", f: "wahe" }], + person: T.Person.SecondSingMale, + }, + ], + ], + }); + expect( + renderVerb({ + verb: wahul, + tense: "habitualPerfectivePast", + subject: T.Person.FirstSingFemale, + object: T.Person.ThirdSingFemale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: true, + vbs: [ + [{ type: "PH", ps: { f: "óo", p: "و" } }], + [ + { + type: "VB", + ps: { + long: [{ p: "وهله", f: "wahula" }], + short: [{ p: "وهه", f: "waha" }], + }, + person: T.Person.ThirdSingFemale, + }, + ], + ], + }); }); test("basic tenses with inflecting roots/stems", () => { - expect(renderVerb({ - verb: bandawul, - tense: "subjunctiveVerb", - person: T.Person.FirstSingMale, - complementGenNum: personToGenNum(T.Person.ThirdSingFemale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: false, - vbs: [ - [ - { - type: "NComp", - comp: { - type: "AdjComp", - ps: { p: "بنده", f: "bánda" }, - gender: "fem", - number: "singular", - }, - }, - ], - [ - { - type: "VB", - ps: { - long: [{ p: "کړم", f: "kRum" }], - short: [{ p: "کم", f: "kum" }], - }, - person: T.Person.FirstSingMale, - }, - ], - ], - }); + expect( + renderVerb({ + verb: bandawul, + tense: "subjunctiveVerb", + subject: T.Person.FirstSingMale, + object: T.Person.ThirdSingFemale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + vbs: [ + [ + { + type: "NComp", + comp: { + type: "AdjComp", + ps: { p: "بنده", f: "bánda" }, + gender: "fem", + number: "singular", + }, + }, + ], + [ + { + type: "VB", + ps: { + long: [{ p: "کړم", f: "kRum" }], + short: [{ p: "کم", f: "kum" }], + }, + person: T.Person.FirstSingMale, + }, + ], + ], + }); }); test("special endings", () => { - const tests: { - verb: T.VerbEntryNoFVars, - tense: "perfectivePast" | "imperfectivePast", - result: T.VerbRenderedOutput, - }[] = [ - // verbs ending in -awul - { - verb: achawul, - tense: "perfectivePast", - result: [ - [{ type: "PH", ps: { p: "وا", f: "wáa" }}], - [ - { - type: "VB", - ps: { - long: [{ p: "چولو", f: "chawulo" }], - short: [{ p: "چاوه", f: "chaawu" }], - }, - person: 4, - }, - ], - ], - }, - { - verb: achawul, - tense: "imperfectivePast", - result: [ - [], - [ - { - type: "VB", - ps: { - long: [{ p: "اچولو", f: "achawúlo" }], - short: [{ p: "اچاوه", f: "achaawú" }], - }, - person: 4, - }, - ], - ], - }, - // verbs with special tppp - { - verb: ganul, - tense: "perfectivePast", - result: [ - [ooPh], - [ - { - type: "VB", - ps: { - long: [{ p: "ګڼلو", f: "gaNulo" }], - short: [{ p: "ګاڼه", f: "gaaNu" }], - }, - person: 4, - }, - ], - ], - }, - { - verb: ganul, - tense: "imperfectivePast", - result: [ - [], - [ - { - type: "VB", - ps: { - long: [{ p: "ګڼلو", f: "gaNúlo" }], - short: [{ p: "ګاڼه", f: "gaaNú" }], - }, - person: 4, - }, - ], - ], - }, - // verbs with special tppp ending in a consonant - { - verb: khatul, - tense: "perfectivePast", - result: [ - [ooPh], - [ - { - type: "VB", - ps: { - long: [{ p: "ختلو", f: "khatulo" }], - short: [ - { p: "خوت", f: "khot" }, - // // TODO: is this even right? - // { p: "خوته", f: "khotu" }, - // { p: "خوتو", f: "khoto" }, - ], - }, - person: 4, - }, - ], - ], - }, - { - verb: leedul, - tense: "imperfectivePast", - result: [ - [], - [ - { - type: "VB", - ps: { - long: [{ p: "لیدلو", f: "leedúlo" }], - short: [ - { p: "لید", f: "léed" }, - // // TODO: is this even right? - // { p: "خوته", f: "khotu" }, - // { p: "خوتو", f: "khoto" }, - ], - }, - person: 4, - }, - ], - ], - }, - { - verb: raawrul, - tense: "perfectivePast", - result: [ - [{ type: "PH", ps: { p: "را", f: "ráa" }}], - [ - { - type: "VB", - ps: { - long: [{ p: "وړلو", f: "wRulo" }], - short: [{ p: "ووړ", f: "woR" }], - }, - person: 4, - }, - ], - ], - }, - // verbs ending in a dental ت or د - { - verb: rasedul, - tense: "perfectivePast", - result: [ - [ooPh], - [ - { - type: "VB", - ps: { - long: [{ p: "رسېدلو", f: "rasedulo" }], - short: [ - { p: "رسېد", f: "rased" }, - { p: "رسېده", f: "rasedu" }, - { p: "رسېدو", f: "rasedo" }, - ], - }, - person: 4, - }, - ], - ], - }, - { - verb: awuxtul, - tense: "imperfectivePast", - result: [ - [], - [ - { - type: "VB", - ps: { - long: [{ p: "اوښتلو", f: "awUxtúlo" }], - short: [ - { p: "اوښت", f: "awÚxt" }, - { p: "اوښته", f: "awUxtú" }, - { p: "اوښتو", f: "awUxtó" }, - ], - }, - person: 4, - }, - ], - ], - }, - // other verbs - { - verb: weshul, - tense: "perfectivePast", - result: [ - [ooPh], - [ - { - type: "VB", - ps: { - long: [{ p: "وېشلو", f: "weshulo" }], - short: [ - { p: "وېشه", f: "weshu" }, - { p: "وېشو", f: "wesho" }, - ], - }, - person: 4, - }, - ], - ], - }, - { - verb: kedulStat, - tense: "perfectivePast", - result: [ - [], - [ - { - type: "VB", - ps: { - long: [{ p: "شولو", f: "shwulo" }], - short: [{ p: "شو", f: "sho" }], - }, - person: T.Person.ThirdSingMale, - }, - ], - ], - }, - { - verb: kedulStat, - tense: "imperfectivePast", - result: [ - [], - [ - { - type: "VB", - ps: { - long: [{ p: "کېدلو", f: "kedúlo" }], - short: [{ p: "کېده", f: "kedú" }], - }, - person: T.Person.ThirdSingMale, - }, - ], - ], - }, - { - verb: kawulStat, - tense: "perfectivePast", - result: [ - [], - [ - { - type: "VB", - ps: { - long: [{ p: "کړلو", f: "kRulo" }], - short: [ - { p: "کړ", f: "kuR" }, - { p: "کړه", f: "kRu" }, - { p: "کړو", f: "kRo" }, - ], - mini: [{ p: "که", f: "ku" }, { p: "کو", f: "ko" }], - }, - person: T.Person.ThirdSingMale, - }, - ], - ], - }, - { - verb: kawulDyn, - tense: "perfectivePast", - result: [ - [ooPh], - [ - { - type: "VB", - ps: { - long: [{ p: "کړلو", f: "kRulo" }], - short: [ - { p: "کړ", f: "kuR" }, - { p: "کړه", f: "kRu" }, - { p: "کړو", f: "kRo" }, - ], - mini: [{ p: "که", f: "ku" }, { p: "کو", f: "ko" }], - }, - person: T.Person.ThirdSingMale, - }, - ], - ], - }, - { - verb: kawulDyn, - tense: "imperfectivePast", - result: [ - [], - [ - { - type: "VB", - ps: { - long: [{ p: "کولو", f: "kawúlo" }], - short: [{ p: "کاوه", f: "kaawú" }], - }, - person: T.Person.ThirdSingMale, - }, - ], - ], - }, - { - verb: tlul, - tense: "imperfectivePast", - result: [ - [], - [ - { - type: "VB", - ps: { - long: [{ p: "تللو", f: "tlúlo" }], - short: [{ p: "ته", f: "tú" }], - }, - person: T.Person.ThirdSingMale, - }, - ], - ], - }, - { - verb: tlul, - tense: "imperfectivePast", - result: [ - [], - [ - { - type: "VB", - ps: { - long: [{ p: "تللو", f: "tlúlo" }], - short: [{ p: "ته", f: "tú" }], - }, - person: T.Person.ThirdSingMale, - }, - ], - ], - }, - { - verb: raatlul, - tense: "imperfectivePast", - result: [ - [], - [ - { - type: "VB", - ps: { - long: [{ p: "راتللو", f: "raatlúlo" }], - short: [{ p: "راته", f: "raatú" }], - }, - person: T.Person.ThirdSingMale, - }, - ], - ], - }, - { - verb: raatlul, - tense: "perfectivePast", - result: [ - [{ type: "PH", ps: { p: "را", f: "ráa" }}], - [ - { - type: "VB", - ps: [{ p: "غی", f: "ghey" }], - person: T.Person.ThirdSingMale, - }, - ], - ], - }, - { - verb: wartlul, - tense: "perfectivePast", - result: [ - [{ type: "PH", ps: { p: "ور", f: "wár" }}], - [ - { - type: "VB", - ps: [{ p: "غی", f: "ghey" }], - person: T.Person.ThirdSingMale, - }, - ], - ], - }, - ]; - tests.forEach(x => { - expect(renderVerb({ - verb: x.verb, - tense: x.tense, + const tests: { + verb: T.VerbEntryNoFVars; + tense: "perfectivePast" | "imperfectivePast"; + result: T.VerbRenderedOutput; + }[] = [ + // verbs ending in -awul + { + verb: achawul, + tense: "perfectivePast", + result: [ + [{ type: "PH", ps: { p: "وا", f: "wáa" } }], + [ + { + type: "VB", + ps: { + long: [{ p: "چولو", f: "chawulo" }], + short: [{ p: "چاوه", f: "chaawu" }], + }, + person: 4, + }, + ], + ], + }, + { + verb: achawul, + tense: "imperfectivePast", + result: [ + [], + [ + { + type: "VB", + ps: { + long: [{ p: "اچولو", f: "achawúlo" }], + short: [{ p: "اچاوه", f: "achaawú" }], + }, + person: 4, + }, + ], + ], + }, + // verbs with special tppp + { + verb: ganul, + tense: "perfectivePast", + result: [ + [ooPh], + [ + { + type: "VB", + ps: { + long: [{ p: "ګڼلو", f: "gaNulo" }], + short: [{ p: "ګاڼه", f: "gaaNu" }], + }, + person: 4, + }, + ], + ], + }, + { + verb: ganul, + tense: "imperfectivePast", + result: [ + [], + [ + { + type: "VB", + ps: { + long: [{ p: "ګڼلو", f: "gaNúlo" }], + short: [{ p: "ګاڼه", f: "gaaNú" }], + }, + person: 4, + }, + ], + ], + }, + // verbs with special tppp ending in a consonant + { + verb: khatul, + tense: "perfectivePast", + result: [ + [ooPh], + [ + { + type: "VB", + ps: { + long: [{ p: "ختلو", f: "khatulo" }], + short: [ + { p: "خوت", f: "khot" }, + // // TODO: is this even right? + // { p: "خوته", f: "khotu" }, + // { p: "خوتو", f: "khoto" }, + ], + }, + person: 4, + }, + ], + ], + }, + { + verb: leedul, + tense: "imperfectivePast", + result: [ + [], + [ + { + type: "VB", + ps: { + long: [{ p: "لیدلو", f: "leedúlo" }], + short: [ + { p: "لید", f: "léed" }, + // // TODO: is this even right? + // { p: "خوته", f: "khotu" }, + // { p: "خوتو", f: "khoto" }, + ], + }, + person: 4, + }, + ], + ], + }, + { + verb: raawrul, + tense: "perfectivePast", + result: [ + [{ type: "PH", ps: { p: "را", f: "ráa" } }], + [ + { + type: "VB", + ps: { + long: [{ p: "وړلو", f: "wRulo" }], + short: [{ p: "ووړ", f: "woR" }], + }, + person: 4, + }, + ], + ], + }, + // verbs ending in a dental ت or د + { + verb: rasedul, + tense: "perfectivePast", + result: [ + [ooPh], + [ + { + type: "VB", + ps: { + long: [{ p: "رسېدلو", f: "rasedulo" }], + short: [ + { p: "رسېد", f: "rased" }, + { p: "رسېده", f: "rasedu" }, + { p: "رسېدو", f: "rasedo" }, + ], + }, + person: 4, + }, + ], + ], + }, + { + verb: awuxtul, + tense: "imperfectivePast", + result: [ + [], + [ + { + type: "VB", + ps: { + long: [{ p: "اوښتلو", f: "awUxtúlo" }], + short: [ + { p: "اوښت", f: "awÚxt" }, + { p: "اوښته", f: "awUxtú" }, + { p: "اوښتو", f: "awUxtó" }, + ], + }, + person: 4, + }, + ], + ], + }, + // other verbs + { + verb: weshul, + tense: "perfectivePast", + result: [ + [ooPh], + [ + { + type: "VB", + ps: { + long: [{ p: "وېشلو", f: "weshulo" }], + short: [ + { p: "وېشه", f: "weshu" }, + { p: "وېشو", f: "wesho" }, + ], + }, + person: 4, + }, + ], + ], + }, + { + verb: kedulStat, + tense: "perfectivePast", + result: [ + [], + [ + { + type: "VB", + ps: { + long: [{ p: "شولو", f: "shwulo" }], + short: [{ p: "شو", f: "sho" }], + }, person: T.Person.ThirdSingMale, - complementGenNum: personToGenNum(T.Person.ThirdSingMale), - voice: "active", - negative: false, - })).toEqual({ hasBa: false, vbs: x.result }); - }); + }, + ], + ], + }, + { + verb: kedulStat, + tense: "imperfectivePast", + result: [ + [], + [ + { + type: "VB", + ps: { + long: [{ p: "کېدلو", f: "kedúlo" }], + short: [{ p: "کېده", f: "kedú" }], + }, + person: T.Person.ThirdSingMale, + }, + ], + ], + }, + { + verb: kawulStat, + tense: "perfectivePast", + result: [ + [], + [ + { + type: "VB", + ps: { + long: [{ p: "کړلو", f: "kRulo" }], + short: [ + { p: "کړ", f: "kuR" }, + { p: "کړه", f: "kRu" }, + { p: "کړو", f: "kRo" }, + ], + mini: [ + { p: "که", f: "ku" }, + { p: "کو", f: "ko" }, + ], + }, + person: T.Person.ThirdSingMale, + }, + ], + ], + }, + { + verb: kawulDyn, + tense: "perfectivePast", + result: [ + [ooPh], + [ + { + type: "VB", + ps: { + long: [{ p: "کړلو", f: "kRulo" }], + short: [ + { p: "کړ", f: "kuR" }, + { p: "کړه", f: "kRu" }, + { p: "کړو", f: "kRo" }, + ], + mini: [ + { p: "که", f: "ku" }, + { p: "کو", f: "ko" }, + ], + }, + person: T.Person.ThirdSingMale, + }, + ], + ], + }, + { + verb: kawulDyn, + tense: "imperfectivePast", + result: [ + [], + [ + { + type: "VB", + ps: { + long: [{ p: "کولو", f: "kawúlo" }], + short: [{ p: "کاوه", f: "kaawú" }], + }, + person: T.Person.ThirdSingMale, + }, + ], + ], + }, + { + verb: tlul, + tense: "imperfectivePast", + result: [ + [], + [ + { + type: "VB", + ps: { + long: [{ p: "تللو", f: "tlúlo" }], + short: [ + { p: "ته", f: "tú" }, + { + p: "تلو", + f: "tló", + }, + ], + }, + person: T.Person.ThirdSingMale, + }, + ], + ], + }, + { + verb: raatlul, + tense: "imperfectivePast", + result: [ + [], + [ + { + type: "VB", + ps: { + long: [{ p: "راتللو", f: "raatlúlo" }], + short: [ + { p: "راته", f: "raatú" }, + { p: "راتلو", f: "raatló" }, + ], + }, + person: T.Person.ThirdSingMale, + }, + ], + ], + }, + { + verb: raatlul, + tense: "perfectivePast", + result: [ + [{ type: "PH", ps: { p: "را", f: "ráa" } }], + [ + { + type: "VB", + ps: [{ p: "غی", f: "ghey" }], + person: T.Person.ThirdSingMale, + }, + ], + ], + }, + { + verb: wartlul, + tense: "perfectivePast", + result: [ + [{ type: "PH", ps: { p: "ور", f: "wár" } }], + [ + { + type: "VB", + ps: [{ p: "غی", f: "ghey" }], + person: T.Person.ThirdSingMale, + }, + ], + ], + }, + ]; + tests.forEach((x) => { + expect( + renderVerb({ + verb: x.verb, + tense: x.tense, + subject: T.Person.ThirdSingMale, + object: x.verb.entry.c.includes("intrans.") + ? undefined + : T.Person.ThirdSingMale, + voice: "active", + negative: false, + }) + ).toEqual({ hasBa: false, vbs: x.result }); + }); - expect(renderVerb({ - verb: kedulStat, - tense: "perfectivePast", - person: T.Person.FirstPlurMale, - complementGenNum: personToGenNum(T.Person.FirstPlurMale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: false, - vbs: [ - [], - [ - { - type: "VB", - ps: { - long: [{ p: "شولو", f: "shwuloo" }], - short: [{ p: "شو", f: "shoo" }], - }, - person: T.Person.FirstPlurMale, - }, - ], - ], - }); - expect(renderVerb({ - verb: tlul, - tense: "imperfectivePast", - person: T.Person.FirstSingMale, - complementGenNum: personToGenNum(T.Person.FirstSingMale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: false, - vbs: [ - [], - [ - { - type: "VB", - ps: { - long: [{ p: "تللم", f: "tlúlum" }], - // TODO: Shouldn't be accent here on single syllable - short: [{ p: "تلم", f: "tlúm" }], - }, - person: T.Person.FirstSingMale, - }, - ], - ], - }); - expect(renderVerb({ - verb: tlul, - tense: "imperfectivePast", - person: T.Person.ThirdSingFemale, - complementGenNum: personToGenNum(T.Person.ThirdSingFemale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: false, - vbs: [ - [], - [ - { - type: "VB", - ps: { - long: [{ p: "تلله", f: "tlúla" }], - // TODO: Shouldn't be accent here on single syllable - short: [{ p: "تله", f: "tlá" }], - }, - person: T.Person.ThirdSingFemale, - }, - ], - ], - }); - // avoid redundant ل ending - expect(renderVerb({ - verb: khorul, - tense: "imperfectivePast", - person: T.Person.ThirdPlurMale, - complementGenNum: personToGenNum(T.Person.ThirdPlurMale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: false, - vbs: [ - [], - [ - { - type: "VB", - ps: { - long: [{ p: "خوړل", f: "khoRúl" }], - short: [{ p: "خوړل", f: "khoRúl" }], - }, - person: T.Person.ThirdPlurMale, - }, - ], - ], - }); - expect(renderVerb({ - verb: khorul, - tense: "perfectivePast", - person: T.Person.ThirdPlurMale, - complementGenNum: personToGenNum(T.Person.ThirdPlurMale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: false, - vbs: [ - [ooPh], - [ - { - type: "VB", - ps: { - long: [{ p: "خوړل", f: "khoRul" }], - short: [{ p: "خوړل", f: "khoRul" }], - }, - person: T.Person.ThirdPlurMale, - }, - ], - ], - }); + expect( + renderVerb({ + verb: kedulStat, + tense: "perfectivePast", + subject: T.Person.FirstPlurMale, + object: undefined, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + vbs: [ + [], + [ + { + type: "VB", + ps: { + long: [{ p: "شولو", f: "shwuloo" }], + short: [{ p: "شو", f: "shoo" }], + }, + person: T.Person.FirstPlurMale, + }, + ], + ], + }); + expect( + renderVerb({ + verb: tlul, + tense: "imperfectivePast", + subject: T.Person.FirstSingMale, + object: undefined, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + vbs: [ + [], + [ + { + type: "VB", + ps: { + long: [{ p: "تللم", f: "tlúlum" }], + // TODO: Shouldn't be accent here on single syllable + short: [{ p: "تلم", f: "tlúm" }], + }, + person: T.Person.FirstSingMale, + }, + ], + ], + }); + expect( + renderVerb({ + verb: tlul, + tense: "imperfectivePast", + subject: T.Person.ThirdSingFemale, + object: undefined, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + vbs: [ + [], + [ + { + type: "VB", + ps: { + long: [{ p: "تلله", f: "tlúla" }], + // TODO: Shouldn't be accent here on single syllable + short: [{ p: "تله", f: "tlá" }], + }, + person: T.Person.ThirdSingFemale, + }, + ], + ], + }); + // avoid redundant ل ending + expect( + renderVerb({ + verb: khorul, + tense: "imperfectivePast", + subject: T.Person.FirstSingMale, + object: T.Person.ThirdPlurMale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + vbs: [ + [], + [ + { + type: "VB", + ps: { + long: [{ p: "خوړل", f: "khoRúl" }], + short: [{ p: "خوړل", f: "khoRúl" }], + }, + person: T.Person.ThirdPlurMale, + }, + ], + ], + }); + expect( + renderVerb({ + verb: khorul, + tense: "perfectivePast", + subject: T.Person.FirstSingMale, + object: T.Person.ThirdPlurMale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + vbs: [ + [ooPh], + [ + { + type: "VB", + ps: { + long: [{ p: "خوړل", f: "khoRul" }], + short: [{ p: "خوړل", f: "khoRul" }], + }, + person: T.Person.ThirdPlurMale, + }, + ], + ], + }); }); test("imperative tenses", () => { - expect(renderVerb({ - verb: wahul, - tense: "imperfectiveImperative", - person: T.Person.SecondSingMale, - complementGenNum: personToGenNum(T.Person.SecondSingMale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: false, - vbs: [ - [], - [ - { type: "VB", ps: [{ p: "وهه", f: "wahá" } ], person: 2}, - ], - ], - }); - expect(renderVerb({ - verb: wahul, - tense: "perfectiveImperative", - person: T.Person.SecondSingFemale, - complementGenNum: personToGenNum(T.Person.SecondSingFemale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: false, - vbs: [ - [ooPh], - [ - { type: "VB", ps: [{ p: "وهه", f: "waha" }], person: 3 }, - ], - ], - }); - expect(renderVerb({ - verb: wahul, - tense: "imperfectiveImperative", - person: T.Person.SecondPlurMale, - complementGenNum: personToGenNum(T.Person.SecondPlurMale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: false, - vbs: [ - [], - [ - { type: "VB", ps: [{ p: "وهئ", f: "wahéyy" } ], person: 8 }, - ], - ], - }); - expect(renderVerb({ - verb: wahul, - tense: "perfectiveImperative", - person: T.Person.SecondPlurFemale, - complementGenNum: personToGenNum(T.Person.SecondPlurFemale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: false, - vbs: [ - [ooPh], - [ - { type: "VB", ps: [{ p: "وهئ", f: "waheyy" }], person: 9 }, - ], - ], - }); + expect( + renderVerb({ + verb: wahul, + tense: "imperfectiveImperative", + subject: T.Person.SecondSingMale, + object: T.Person.ThirdSingMale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + vbs: [[], [{ type: "VB", ps: [{ p: "وهه", f: "wahá" }], person: 2 }]], + }); + expect( + renderVerb({ + verb: wahul, + tense: "perfectiveImperative", + subject: T.Person.SecondSingFemale, + object: T.Person.ThirdSingMale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + vbs: [[ooPh], [{ type: "VB", ps: [{ p: "وهه", f: "waha" }], person: 3 }]], + }); + expect( + renderVerb({ + verb: wahul, + tense: "imperfectiveImperative", + subject: T.Person.SecondPlurMale, + object: T.Person.ThirdSingMale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + vbs: [[], [{ type: "VB", ps: [{ p: "وهئ", f: "wahéyy" }], person: 8 }]], + }); + expect( + renderVerb({ + verb: wahul, + tense: "perfectiveImperative", + subject: T.Person.SecondPlurFemale, + object: T.Person.ThirdSingMale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + vbs: [[ooPh], [{ type: "VB", ps: [{ p: "وهئ", f: "waheyy" }], person: 9 }]], + }); }); test("ability tenses", () => { - expect(renderVerb({ - verb: wahul, - tense: "presentVerbModal", - person: T.Person.FirstSingMale, - complementGenNum: personToGenNum(T.Person.FirstSingMale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: false, - vbs: [ - [], - [ - { - type: "VB", - ps: { - long: [ - { p: "وهلی", f: "wahúley" }, - { p: "وهلای", f: "wahúlaay" }, - ], - short: [ - { p: "وهی", f: "wahéy" }, - { p: "وهای", f: "waháay" }, - ], - }, - }, - { - type: "VB", - ps: [{ p: "شم", f: "shum" }], - person: T.Person.FirstSingMale, - }, + expect( + renderVerb({ + verb: wahul, + tense: "presentVerbModal", + subject: T.Person.FirstSingMale, + object: T.Person.ThirdSingMale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + vbs: [ + [], + [ + { + type: "VB", + ps: { + long: [ + { p: "وهلی", f: "wahúley" }, + { p: "وهلای", f: "wahúlaay" }, ], - ], - }); -}); - -test("basic tenses", () => { - expect(renderVerb({ - verb: wahul, - tense: "presentVerb", - person: T.Person.FirstSingMale, - complementGenNum: personToGenNum(T.Person.FirstSingMale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: false, - - vbs: [ - [], - [ - { type: "VB", ps: [{ p: "وهم", f: "wahum" }], person: T.Person.FirstSingMale }, + short: [ + { p: "وهی", f: "wahéy" }, + { p: "وهای", f: "waháay" }, ], - ], - }); - expect(renderVerb({ - verb: wahul, - tense: "subjunctiveVerb", - person: T.Person.SecondSingMale, - complementGenNum: personToGenNum(T.Person.SecondSingMale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: false, - vbs: [ - [{ type: "PH", ps: { f: "óo", p: "و" }}], - [ - { type: "VB", ps: [{ p: "وهې", f: "wahe" }], person: T.Person.SecondSingMale }, - ], - ], - }); - expect(renderVerb({ - verb: wahul, - tense: "habitualPerfectivePast", - person: T.Person.ThirdSingFemale, - complementGenNum: personToGenNum(T.Person.ThirdSingFemale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: true, - vbs: [ - [{ type: "PH", ps: { f: "óo", p: "و" }}], - [ - { - type: "VB", - ps: { - long: [{ p: "وهله", f: "wahula" }], - short: [{ p: "وهه", f: "waha" }], - }, - person: T.Person.ThirdSingFemale - }, - ], - ], - }); + }, + }, + { + type: "VB", + ps: [{ p: "شم", f: "shum" }], + person: T.Person.FirstSingMale, + }, + ], + ], + }); }); test("perfect tenses", () => { - expect(renderVerb({ - verb: wahul, - tense: "presentPerfect", - person: T.Person.FirstSingMale, - complementGenNum: personToGenNum(T.Person.FirstSingMale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: false, - vbs: [ - [], - [ - { type: "VB", ps: [{ p: "وهلی", f: "wahúley" }], gender: "masc", number: "singular" }, - { type: "VB", ps: [{ p: "یم", f: "yum" }], person: T.Person.FirstSingMale }, - ], - ], - }); - expect(renderVerb({ - verb: wahul, - tense: "subjunctivePerfect", - person: T.Person.FirstSingMale, - complementGenNum: personToGenNum(T.Person.FirstSingMale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: false, - vbs: [ - [], - [ - { type: "VB", ps: [{ p: "وهلی", f: "wahúley" }], gender: "masc", number: "singular" }, - { type: "VB", ps: [{ p: "وم", f: "wum" }], person: T.Person.FirstSingMale }, - ], - ], - }); - expect(renderVerb({ - verb: wahul, - tense: "habitualPerfect", - person: T.Person.FirstSingMale, - complementGenNum: personToGenNum(T.Person.FirstSingMale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: false, - vbs: [ - [], - [ - { type: "VB", ps: [{ p: "وهلی", f: "wahúley" }], gender: "masc", number: "singular" }, - { type: "VB", ps: [{ p: "یم", f: "yum" }], person: T.Person.FirstSingMale }, - ], - ], - }); - expect(renderVerb({ - verb: wahul, - tense: "habitualPerfect", - person: T.Person.ThirdPlurMale, - complementGenNum: personToGenNum(T.Person.ThirdPlurMale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: false, - vbs: [ - [], - [ - { type: "VB", ps: [{ p: "وهلي", f: "wahúlee" }], gender: "masc", number: "plural" }, - { type: "VB", ps: [{ p: "وي", f: "wee" }], person: T.Person.ThirdPlurMale }, - ], - ], - }); - expect(renderVerb({ - verb: wahul, - tense: "futurePerfect", - person: T.Person.FirstSingMale, - complementGenNum: personToGenNum(T.Person.FirstSingMale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: true, - vbs: [ - [], - [ - { type: "VB", ps: [{ p: "وهلی", f: "wahúley" }], gender: "masc", number: "singular" }, - { type: "VB", ps: [{ p: "یم", f: "yum" }], person: T.Person.FirstSingMale }, - ], - ], - }); - expect(renderVerb({ - verb: wahul, - tense: "pastPerfect", - person: T.Person.SecondSingFemale, - complementGenNum: personToGenNum(T.Person.SecondSingFemale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: false, - vbs: [ - [], - [ - { type: "VB", ps: [{ p: "وهلې", f: "wahúle" }], gender: "fem", number: "singular" }, - { - type: "VB", - ps: { - long: [{ p: "ولې", f: "wule" }], - short: [{ p: "وې", f: "we" }], - }, - person: T.Person.SecondSingFemale, - }, - ], - ], - }); - expect(renderVerb({ - verb: wahul, - tense: "wouldBePerfect", - person: T.Person.SecondSingFemale, - complementGenNum: personToGenNum(T.Person.SecondSingFemale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: true, - vbs: [ - [], - [ - { type: "VB", ps: [{ p: "وهلې", f: "wahúle" }], gender: "fem", number: "singular" }, - { - type: "VB", - ps: { - long: [{ p: "ولې", f: "wule" }], - short: [{ p: "وې", f: "we" }], - }, - person: T.Person.SecondSingFemale, - }, - ], - ], - }); - expect(renderVerb({ - verb: wahul, - tense: "pastSubjunctivePerfect", - person: T.Person.SecondSingFemale, - complementGenNum: personToGenNum(T.Person.SecondSingFemale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: false, - vbs: [ - [], - [ - { type: "VB", ps: [{ p: "وهلې", f: "wahúle" }], gender: "fem", number: "singular" }, - { - type: "VB", - ps: [{ p: "وای", f: "waay" }, { p: "وی", f: "wey" }], - person: T.Person.SecondSingFemale, - }, - ], - ], - }); - expect(renderVerb({ - verb: wahul, - tense: "wouldHaveBeenPerfect", - person: T.Person.SecondSingFemale, - complementGenNum: personToGenNum(T.Person.SecondSingFemale), - voice: "active", - negative: false, - })).toEqual({ - hasBa: true, - vbs: [ - [], - [ - { type: "VB", ps: [{ p: "وهلې", f: "wahúle" }], gender: "fem", number: "singular" }, - { - type: "VB", - ps: [{ p: "وای", f: "waay" }, { p: "وی", f: "wey" }], - person: T.Person.SecondSingFemale, - }, - ], - ], - }); + expect( + renderVerb({ + verb: wahul, + tense: "presentPerfect", + subject: T.Person.SecondSingMale, + object: T.Person.FirstSingMale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + vbs: [ + [], + [ + { + type: "VB", + ps: [{ p: "وهلی", f: "wahúley" }], + gender: "masc", + number: "singular", + }, + { + type: "VB", + ps: [{ p: "یم", f: "yum" }], + person: T.Person.FirstSingMale, + }, + ], + ], + }); + expect( + renderVerb({ + verb: wahul, + tense: "subjunctivePerfect", + subject: T.Person.ThirdPlurFemale, + object: T.Person.FirstSingMale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + vbs: [ + [], + [ + { + type: "VB", + ps: [{ p: "وهلی", f: "wahúley" }], + gender: "masc", + number: "singular", + }, + { + type: "VB", + ps: [{ p: "وم", f: "wum" }], + person: T.Person.FirstSingMale, + }, + ], + ], + }); + expect( + renderVerb({ + verb: wahul, + tense: "habitualPerfect", + object: T.Person.FirstSingMale, + subject: T.Person.ThirdSingMale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + vbs: [ + [], + [ + { + type: "VB", + ps: [{ p: "وهلی", f: "wahúley" }], + gender: "masc", + number: "singular", + }, + { + type: "VB", + ps: [{ p: "یم", f: "yum" }], + person: T.Person.FirstSingMale, + }, + ], + ], + }); + expect( + renderVerb({ + verb: wahul, + tense: "habitualPerfect", + subject: T.Person.FirstPlurMale, + object: T.Person.ThirdPlurMale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + vbs: [ + [], + [ + { + type: "VB", + ps: [{ p: "وهلي", f: "wahúlee" }], + gender: "masc", + number: "plural", + }, + { + type: "VB", + ps: [{ p: "وي", f: "wee" }], + person: T.Person.ThirdPlurMale, + }, + ], + ], + }); + expect( + renderVerb({ + verb: wahul, + tense: "futurePerfect", + object: T.Person.FirstSingMale, + subject: T.Person.ThirdPlurMale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: true, + vbs: [ + [], + [ + { + type: "VB", + ps: [{ p: "وهلی", f: "wahúley" }], + gender: "masc", + number: "singular", + }, + { + type: "VB", + ps: [{ p: "یم", f: "yum" }], + person: T.Person.FirstSingMale, + }, + ], + ], + }); + expect( + renderVerb({ + verb: wahul, + tense: "pastPerfect", + subject: T.Person.FirstPlurFemale, + object: T.Person.SecondSingFemale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + vbs: [ + [], + [ + { + type: "VB", + ps: [{ p: "وهلې", f: "wahúle" }], + gender: "fem", + number: "singular", + }, + { + type: "VB", + ps: { + long: [{ p: "ولې", f: "wule" }], + short: [{ p: "وې", f: "we" }], + }, + person: T.Person.SecondSingFemale, + }, + ], + ], + }); + expect( + renderVerb({ + verb: wahul, + tense: "wouldBePerfect", + subject: T.Person.ThirdSingMale, + object: T.Person.SecondSingFemale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: true, + vbs: [ + [], + [ + { + type: "VB", + ps: [{ p: "وهلې", f: "wahúle" }], + gender: "fem", + number: "singular", + }, + { + type: "VB", + ps: { + long: [{ p: "ولې", f: "wule" }], + short: [{ p: "وې", f: "we" }], + }, + person: T.Person.SecondSingFemale, + }, + ], + ], + }); + expect( + renderVerb({ + verb: wahul, + tense: "pastSubjunctivePerfect", + object: T.Person.SecondSingFemale, + subject: T.Person.FirstPlurMale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + vbs: [ + [], + [ + { + type: "VB", + ps: [{ p: "وهلې", f: "wahúle" }], + gender: "fem", + number: "singular", + }, + { + type: "VB", + ps: [ + { p: "وای", f: "waay" }, + { p: "وی", f: "wey" }, + ], + person: T.Person.SecondSingFemale, + }, + ], + ], + }); + expect( + renderVerb({ + verb: wahul, + tense: "wouldHaveBeenPerfect", + subject: T.Person.FirstSingMale, + object: T.Person.SecondSingFemale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: true, + vbs: [ + [], + [ + { + type: "VB", + ps: [{ p: "وهلې", f: "wahúle" }], + gender: "fem", + number: "singular", + }, + { + type: "VB", + ps: [ + { p: "وای", f: "waay" }, + { p: "وی", f: "wey" }, + ], + person: T.Person.SecondSingFemale, + }, + ], + ], + }); }); test("ending on complex verbs", () => { - expect(renderVerb({ - verb: stureyKawul, - tense: "presentVerbModal", - person: T.Person.SecondSingMale, - voice: "active", - complementGenNum: personToGenNum(T.Person.ThirdSingFemale), - negative: false, - })).toEqual({ - hasBa: false, - vbs: [ - [], - [ - { - type: "welded", - left: { - type: "NComp", - comp: { - type: "AdjComp", - ps: { p: "ستړې", f: "stuRe" }, - gender: "fem", - number: "singular", - }, - }, - right: { - type: "VB", - ps: { - long: [ - { p: "کولی", f: "kawúley" }, - { p: "کولای", f: "kawúlaay" }, - ], - short: [ - { p: "کوی", f: "kawéy" }, - { p: "کوای", f: "kawáay" }, - ], - }, - }, - }, - { - type: "VB", - ps: [{ p: "شې", f: "she" }], - person: T.Person.SecondSingMale, - }, - ], - ], - }); - expect(renderVerb({ - verb: stureyKawul, - tense: "presentVerb", - person: T.Person.SecondSingMale, - voice: "active", - complementGenNum: personToGenNum(T.Person.ThirdSingFemale), - negative: false, - })).toEqual({ - hasBa: false, - vbs: [ - [], - [ - { - type: "welded", - left: { - type: "NComp", - comp: { - type: "AdjComp", - ps: { p: "ستړې", f: "stuRe" }, - gender: "fem", - number: "singular", - }, - }, - right: { - type: "VB", - ps: [{ p: "کوې", f: "kawé" }], - }, - person: T.Person.SecondSingMale, - }, - ], - ], - }); -}); \ No newline at end of file + expect( + renderVerb({ + verb: stureyKawul, + tense: "presentVerbModal", + subject: T.Person.SecondSingMale, + object: T.Person.ThirdSingFemale, + voice: "active", + negative: false, + }) + ).toEqual({ + hasBa: false, + vbs: [ + [], + [ + { + type: "welded", + left: { + type: "NComp", + comp: { + type: "AdjComp", + ps: { p: "ستړې", f: "stuRe" }, + gender: "fem", + number: "singular", + }, + }, + right: { + type: "VB", + ps: { + long: [ + { p: "کولی", f: "kawúley" }, + { p: "کولای", f: "kawúlaay" }, + ], + short: [ + { p: "کوی", f: "kawéy" }, + { p: "کوای", f: "kawáay" }, + ], + }, + }, + }, + { + type: "VB", + ps: [{ p: "شې", f: "she" }], + person: T.Person.SecondSingMale, + }, + ], + ], + }); + expect( + renderVerb({ + verb: stureyKawul, + tense: "presentVerb", + subject: T.Person.SecondSingMale, + voice: "active", + object: T.Person.ThirdSingFemale, + negative: false, + }) + ).toEqual({ + hasBa: false, + vbs: [ + [], + [ + { + type: "welded", + left: { + type: "NComp", + comp: { + type: "AdjComp", + ps: { p: "ستړې", f: "stuRe" }, + gender: "fem", + number: "singular", + }, + }, + right: { + type: "VB", + ps: [{ p: "کوې", f: "kawé" }], + }, + person: T.Person.SecondSingMale, + }, + ], + ], + }); +}); diff --git a/src/lib/src/new-verb-engine/render-verb.ts b/src/lib/src/new-verb-engine/render-verb.ts index 815c7c3..6828a3e 100644 --- a/src/lib/src/new-verb-engine/render-verb.ts +++ b/src/lib/src/new-verb-engine/render-verb.ts @@ -1,310 +1,367 @@ import * as T from "../../../types"; import { - getVerbBlockPosFromPerson, - isSecondPerson, - personGender, - personNumber, - personToGenNum, + getVerbBlockPosFromPerson, + isSecondPerson, + personNumber, + personToGenNum, } from "../misc-helpers"; +import { fmapSingleOrLengthOpts } from "../fmaps"; +import { concatPsString, getLength } from "../p-text-helpers"; import { - fmapSingleOrLengthOpts, -} from "../fmaps"; -import { - concatPsString, - getLength, -} from "../p-text-helpers"; -import { - presentEndings, - pastEndings, - equativeEndings, - imperativeEndings, + presentEndings, + pastEndings, + equativeEndings, + imperativeEndings, } from "../grammar-units"; -import { isKawulVerb, isAbilityTense, isPerfectTense, isTlulVerb, isImperativeTense } from "../type-predicates"; +import { + isKawulVerb, + isAbilityTense, + isPerfectTense, + isTlulVerb, +} from "../type-predicates"; import { perfectTenseHasBa } from "../phrase-building/vp-tools"; import { makePsString, removeFVarients } from "../accent-and-ps-utils"; import { getPastParticiple, getRootStem } from "./roots-and-stems"; -import { isKedul, perfectTenseToEquative, verbEndingConcat } from "./rs-helpers"; -import { accentOnNFromEnd, accentPsSyllable, removeAccents } from "../accent-helpers"; +import { + isKedul, + perfectTenseToEquative, + verbEndingConcat, +} from "./rs-helpers"; +import { + accentOnNFromEnd, + accentPsSyllable, + removeAccents, +} from "../accent-helpers"; -const formulas: Record = { - "presentVerb": { - aspect: "imperfective", - tenseC: "present", - hasBa: false, - }, - "subjunctiveVerb": { - aspect: "imperfective", - tenseC: "present", - hasBa: false, - }, - "perfectiveFuture": { - aspect: "perfective", - tenseC: "present", - hasBa: true, - }, - "imperfectiveFuture": { - aspect: "imperfective", - tenseC: "present", - hasBa: true, - }, - "perfectivePast": { - aspect: "perfective", - tenseC: "past", - hasBa: false, - }, - "imperfectivePast": { - aspect: "imperfective", - tenseC: "past", - hasBa: false, - }, - "habitualImperfectivePast": { - aspect: "imperfective", - tenseC: "past", - hasBa: true, - }, - "habitualPerfectivePast": { - aspect: "perfective", - tenseC: "past", - hasBa: true, - }, - "perfectiveImperative": { - aspect: "perfective", - tenseC: "imperative", - hasBa: false, - }, - "imperfectiveImperative": { - aspect: "imperfective", - tenseC: "imperative", - hasBa: false, - }, -} +const formulas: Record< + T.VerbTense | T.ImperativeTense, + { + aspect: T.Aspect; + tenseC: "past" | "present" | "imperative"; + hasBa: boolean; + } +> = { + presentVerb: { + aspect: "imperfective", + tenseC: "present", + hasBa: false, + }, + subjunctiveVerb: { + aspect: "perfective", + tenseC: "present", + hasBa: false, + }, + perfectiveFuture: { + aspect: "perfective", + tenseC: "present", + hasBa: true, + }, + imperfectiveFuture: { + aspect: "imperfective", + tenseC: "present", + hasBa: true, + }, + perfectivePast: { + aspect: "perfective", + tenseC: "past", + hasBa: false, + }, + imperfectivePast: { + aspect: "imperfective", + tenseC: "past", + hasBa: false, + }, + habitualImperfectivePast: { + aspect: "imperfective", + tenseC: "past", + hasBa: true, + }, + habitualPerfectivePast: { + aspect: "perfective", + tenseC: "past", + hasBa: true, + }, + perfectiveImperative: { + aspect: "perfective", + tenseC: "imperative", + hasBa: false, + }, + imperfectiveImperative: { + aspect: "imperfective", + tenseC: "imperative", + hasBa: false, + }, +}; -// to get the chart of conjugations: -// 1. get the conjugation for all persons -// 2. if transitive present tense, check (or do all the conjugation) the conjugation with all different complement person stuff -// if necessary pull out the object option -// - -// to make the verbs displayable for the charts -// - take the output of renderVerb { hasBa, VerbRenderedOutput } -// - filter out a long and short version etc if necessary -// - pass it into combineIntoText - -// PROBLEM: how to handle when to specify the object -// present tense - -// TODO: problem with laaR - no perfective split -export function renderVerb({ verb, tense: tense, person, voice, negative, complementGenNum }: { - verb: T.VerbEntry, - negative: boolean, - tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense, // TODO: make T.Tense - person: T.Person, - complementGenNum: { gender: T.Gender, number: T.NounNumber }, - voice: T.Voice, -}): { - hasBa: boolean, - vbs: T.VerbRenderedOutput, -} { - if (isPerfectTense(tense)) { - return renderPerfectVerb({ verb, tense, voice, person }); - } - const { aspect, tenseC, hasBa } = formulas[removeAbility(tense)]; - const isPast = tenseC === "past"; - const type = isAbilityTense(tense) ? "ability" : "basic"; - - // #1 get the appropriate root / stem - const [vHead, rest] = getRootStem({ - verb, - rs: isPast ? "root" : "stem", - aspect, - voice, - type, - genderNumber: complementGenNum, +// TODO: dynamic and stative compounds +export function renderVerb({ + verb, + tense, + subject, + object, + voice, +}: { + verb: T.VerbEntry; + negative: boolean; + tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense; // TODO: make T.Tense + subject: T.Person; + object: T.Person | undefined; + voice: T.Voice; +}): T.RenderVerbOutput { + if (isPerfectTense(tense)) { + return renderPerfectVerb({ + verb, + tense, + voice, + person: object ?? subject, }); - // #2 add the verb ending to it - const ending = getEnding(person, tenseC, aspect); - return { - hasBa, - vbs: [ - vHead, - addEnding({ - rs: rest, - ending, - verb, - person, - pastThird: isPast && person === T.Person.ThirdSingMale, - aspect, - basicForm: type === "basic" && voice === "active", - }), - ], - }; + } + const { aspect, tenseC, hasBa } = formulas[removeAbility(tense)]; + const isPast = tenseC === "past"; + const type = isAbilityTense(tense) ? "ability" : "basic"; + const transitive = object !== undefined; + const king = transitive && isPast ? object : subject; + + // #1 get the appropriate root / stem + const [vHead, rest] = getRootStem({ + verb, + rs: isPast ? "root" : "stem", + aspect, + voice, + type, + genderNumber: personToGenNum(transitive ? object : subject), + }); + + // #2 add the verb ending to it + const ending = getEnding(king, tenseC, aspect); + return { + hasBa, + objComp: undefined, + vbs: [ + vHead, + addEnding({ + rs: rest, + ending, + verb, + person: king, + pastThird: isPast && king === T.Person.ThirdSingMale, + aspect, + basicForm: type === "basic" && voice === "active", + }), + ], + }; } -function renderPerfectVerb({ tense, verb, voice, person }: { - tense: T.PerfectTense, - verb: T.VerbEntry, - voice: T.Voice, - person: T.Person, - // TODO: Tighter typing on the output for T.VB (enforce genderNumber?) -}): { hasBa: boolean, vbs: [[], [T.VB, T.VBE]] } { - const hasBa = perfectTenseHasBa(tense); - // #1 get the past participle - const pp = getPastParticiple(verb, voice, personToGenNum(person)); - // #2 get the right equative - const equative = equativeEndings[perfectTenseToEquative(tense)]; - const [row, col] = getVerbBlockPosFromPerson(person); - const equativeBlock: T.VBE = { - type: "VB", - person, - ps: fmapSingleOrLengthOpts(x => x[row][col], equative), - }; - return { - hasBa, - vbs: [[], [pp, equativeBlock]], - }; +function renderPerfectVerb({ + tense, + verb, + voice, + person, +}: { + person: T.Person; + tense: T.PerfectTense; + verb: T.VerbEntry; + voice: T.Voice; +}): { + hasBa: boolean; + vbs: [[], [T.VB, T.VBE]]; + objComp: T.Rendered | undefined; +} { + const hasBa = perfectTenseHasBa(tense); + // #1 get the past participle + const pp = getPastParticiple(verb, voice, personToGenNum(person)); + // #2 get the right equative + const equative = equativeEndings[perfectTenseToEquative(tense)]; + const [row, col] = getVerbBlockPosFromPerson(person); + const equativeBlock: T.VBE = { + type: "VB", + person, + ps: fmapSingleOrLengthOpts((x) => x[row][col], equative), + }; + return { + hasBa, + objComp: undefined, + vbs: [[], [pp, equativeBlock]], + }; } -function addEnding({ verb, rs, ending, person, pastThird, aspect, basicForm }: { - rs: [T.VB, T.VBA] | [T.VBA], - ending: T.SingleOrLengthOpts, - person: T.Person, - verb: T.VerbEntry, - pastThird: boolean, - aspect: T.Aspect, - basicForm: boolean, +function addEnding({ + verb, + rs, + ending, + person, + pastThird, + aspect, + basicForm, +}: { + rs: [T.VB, T.VBA] | [T.VBA]; + ending: T.SingleOrLengthOpts; + person: T.Person; + verb: T.VerbEntry; + pastThird: boolean; + aspect: T.Aspect; + basicForm: boolean; }): [T.VB, T.VBE] | [T.VBE] { - return rs.length === 2 - ? [rs[0], addEnd(rs[1], ending)] - : [addEnd(rs[0], ending)]; - function addEnd(vba: T.VBA, ending: T.SingleOrLengthOpts): T.VBE { - if (vba.type === "welded") { - return { - ...vba, - right: addToVBBasicEnd(vba.right, ending), - person, - }; - } - return { - ...addToVBBasicEnd(vba, ending), - person, - } + return rs.length === 2 + ? [rs[0], addEnd(rs[1], ending)] + : [addEnd(rs[0], ending)]; + function addEnd( + vba: T.VBA, + ending: T.SingleOrLengthOpts + ): T.VBE { + if (vba.type === "welded") { + return { + ...vba, + right: addToVBBasicEnd(vba.right, ending), + person, + }; } - function addToVBBasicEnd(vb: T.VBBasic, end: T.SingleOrLengthOpts): T.VBBasic { - if ("long" in vb.ps) { - if (vb.ps.short[0].f === "ghl" && pastThird && basicForm) { - return { - ...vb, - ps: [{ p: "غی", f: "ghey" }], - }; - } - const endLong = getLength(end, "long"); - const endShort = getLength(end, "short"); - // TODO: this is hacky - return { - ...vb, - ps: { - long: verbEndingConcat(vb.ps.long, endLong), - short: pastThird && basicForm - ? ensure3rdPast(vb.ps.short, endShort, verb, aspect) - : verbEndingConcat(vb.ps.short, endShort), - ...vb.ps.mini ? { - mini: verbEndingConcat(vb.ps.mini, endShort), - } : {}, - }, - }; - } - /* istanbul ignore next */ - if ("long" in end) { - throw new Error("should not be using verb stems with long and short endings"); - } + return { + ...addToVBBasicEnd(vba, ending), + person, + }; + } + function addToVBBasicEnd( + vb: T.VBBasic, + end: T.SingleOrLengthOpts + ): T.VBBasic { + if ("long" in vb.ps) { + if (vb.ps.short[0].f === "ghl" && pastThird && basicForm) { return { - ...vb, - ps: verbEndingConcat(vb.ps, end), + ...vb, + ps: [{ p: "غی", f: "ghey" }], }; + } + const endLong = getLength(end, "long"); + const endShort = getLength(end, "short"); + // TODO: this is hacky + return { + ...vb, + ps: { + long: verbEndingConcat(vb.ps.long, endLong), + short: + pastThird && basicForm + ? ensure3rdPast(vb.ps.short, endShort, verb, aspect) + : verbEndingConcat(vb.ps.short, endShort), + ...(vb.ps.mini + ? { + mini: verbEndingConcat(vb.ps.mini, endShort), + } + : {}), + }, + }; } + /* istanbul ignore next */ + if ("long" in end) { + throw new Error( + "should not be using verb stems with long and short endings" + ); + } + return { + ...vb, + ps: verbEndingConcat(vb.ps, end), + }; + } } -function getEnding(person: T.Person, tenseC: "present" | "past" | "imperative", aspect: T.Aspect) { - if (tenseC === "imperative") { - if (!isSecondPerson(person)) { - throw new Error("imperative forms must be second person"); - } - const number = personNumber(person); - const ends = imperativeEndings[0][number === "singular" ? 0 : 1]; - return aspect === "imperfective" - ? ends.map(e => accentPsSyllable(e)) - : ends; +function getEnding( + person: T.Person, + tenseC: "present" | "past" | "imperative", + aspect: T.Aspect +) { + if (tenseC === "imperative") { + if (!isSecondPerson(person)) { + throw new Error("imperative forms must be second person"); } - const [row, col] = getVerbBlockPosFromPerson(person); - return tenseC === "past" ? { + const number = personNumber(person); + const ends = imperativeEndings[0][number === "singular" ? 0 : 1]; + return aspect === "imperfective" + ? ends.map((e) => accentPsSyllable(e)) + : ends; + } + const [row, col] = getVerbBlockPosFromPerson(person); + return tenseC === "past" + ? { long: pastEndings.long[row][col], short: pastEndings.short[row][col], - } : presentEndings[row][col]; + } + : presentEndings[row][col]; } // TODO: THIS IS PROBABLY SKEEEETCH -function ensure3rdPast(rs: T.PsString[], ending: T.PsString[], verb: T.VerbEntry, aspect: T.Aspect): T.PsString[] { - if (isKedul(verb)) { - return aspect === "perfective" - ? [{ p: "شو", f: "sho" }] - : [{ p: "کېده", f: "kedú" }]; - } - if (isKawulVerb(verb) && rs[0].p === "کړ") { - return [ - { p: "کړ", f: "kuR" }, - { p: "کړه", f: "kRu" }, - { p: "کړو", f: "kRo" }, - ]; - } - if (isTlulVerb(verb)) { - // should be imperfective at this point - // the perfective غی should already be covered in the function this is coming from - return [{ - p: rs[0].p.slice(0, -1) + "ه", - f: rs[0].f.slice(0, -2) + "ú", - }]; - } - if (verb.entry.tppp && verb.entry.tppf) { - const tip = removeAccents(verb.entry.separationAtP !== undefined - ? makePsString(verb.entry.tppp.slice(verb.entry.separationAtP), verb.entry.tppf.slice(verb.entry.separationAtF)) - : makePsString(verb.entry.tppp, verb.entry.tppf)); - const aTip = aspect === "imperfective" - ? accentOnNFromEnd(tip, 0) - : tip; - return [aTip]; - // if it ends in a consonant, the special form will also have another - // variation ending with a ه - u - // const endsInAConsonant = (pashtoConsonants.includes(tip.p.slice(-1)) || tip.f.slice(-1) === "w"); - // return [ - // aTip, - // ...endsInAConsonant ? [ - // ...verbEndingConcat([aTip], [{ p: "ه", f: "u" }, { p: "و", f: "o" }]), - // ] : [], - // ]; - } - const endsInAwul = ( - (["awul", "awúl"].includes(removeFVarients(verb.entry.f).slice(-4))) - && - (verb.entry.p.slice(-2) === "ول") +function ensure3rdPast( + rs: T.PsString[], + ending: T.PsString[], + verb: T.VerbEntry, + aspect: T.Aspect +): T.PsString[] { + if (isKedul(verb)) { + return aspect === "perfective" + ? [{ p: "شو", f: "sho" }] + : [{ p: "کېده", f: "kedú" }]; + } + if (isKawulVerb(verb) && rs[0].p === "کړ") { + return [ + { p: "کړ", f: "kuR" }, + { p: "کړه", f: "kRu" }, + { p: "کړو", f: "kRo" }, + ]; + } + if (isTlulVerb(verb)) { + // should be imperfective at this point + // the perfective غی should already be covered in the function this is coming from + return [ + { + p: rs[0].p.slice(0, -1) + "ه", + f: rs[0].f.slice(0, -2) + "ú", + }, + { + p: rs[0].p + "و", + f: rs[0].f.slice(0, -1) + "ó", + }, + ]; + } + if (verb.entry.tppp && verb.entry.tppf) { + const tip = removeAccents( + verb.entry.separationAtP !== undefined + ? makePsString( + verb.entry.tppp.slice(verb.entry.separationAtP), + verb.entry.tppf.slice(verb.entry.separationAtF) + ) + : makePsString(verb.entry.tppp, verb.entry.tppf) ); - // TODO: check about verbs like tawul (if they exist) - if (endsInAwul) { - const base = { p: rs[0].p.slice(0, -1), f: rs[0].f.slice(0, -2) }; - const aawuEnd = concatPsString(base, { p: "اوه", f: base.f.charAt(base.f.length-1) === "a" ? "awu" : "aawu" }); - return [aspect === "imperfective" - ? accentOnNFromEnd(aawuEnd, 0) - : aawuEnd]; - } - const endsInDental = ["د", "ت"].includes(rs[0].p.slice(-1)); - // short endings like ورسېد - const ends = endsInDental ? [{ p: "", f: "" }, ...ending] : ending; - return verbEndingConcat(rs, ends); + const aTip = aspect === "imperfective" ? accentOnNFromEnd(tip, 0) : tip; + return [aTip]; + // if it ends in a consonant, the special form will also have another + // variation ending with a ه - u + // const endsInAConsonant = (pashtoConsonants.includes(tip.p.slice(-1)) || tip.f.slice(-1) === "w"); + // return [ + // aTip, + // ...endsInAConsonant ? [ + // ...verbEndingConcat([aTip], [{ p: "ه", f: "u" }, { p: "و", f: "o" }]), + // ] : [], + // ]; + } + const endsInAwul = + ["awul", "awúl"].includes(removeFVarients(verb.entry.f).slice(-4)) && + verb.entry.p.slice(-2) === "ول"; + // TODO: check about verbs like tawul (if they exist) + if (endsInAwul) { + const base = { p: rs[0].p.slice(0, -1), f: rs[0].f.slice(0, -2) }; + const aawuEnd = concatPsString(base, { + p: "اوه", + f: base.f.charAt(base.f.length - 1) === "a" ? "awu" : "aawu", + }); + return [aspect === "imperfective" ? accentOnNFromEnd(aawuEnd, 0) : aawuEnd]; + } + const endsInDental = ["د", "ت"].includes(rs[0].p.slice(-1)); + // short endings like ورسېد + const ends = endsInDental ? [{ p: "", f: "" }, ...ending] : ending; + return verbEndingConcat(rs, ends); } -function removeAbility(tense: T.VerbTense | T.AbilityTense | T.ImperativeTense): T.VerbTense | T.ImperativeTense { - return tense.replace("Modal", "") as T.VerbTense | T.ImperativeTense; -} \ No newline at end of file +function removeAbility( + tense: T.VerbTense | T.AbilityTense | T.ImperativeTense +): T.VerbTense | T.ImperativeTense { + return tense.replace("Modal", "") as T.VerbTense | T.ImperativeTense; +} diff --git a/src/lib/src/new-verb-engine/roots-and-stems.test.ts b/src/lib/src/new-verb-engine/roots-and-stems.test.ts index 546398e..779d1c9 100644 --- a/src/lib/src/new-verb-engine/roots-and-stems.test.ts +++ b/src/lib/src/new-verb-engine/roots-and-stems.test.ts @@ -2,76 +2,560 @@ import * as T from "../../../types"; import { getPastParticiple, getRootStem } from "./roots-and-stems"; 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 achawul = vEntry({"ts":1527811872,"i":224,"p":"اچول","f":"achawul","g":"achawul","e":"to put, pour, drop, throw, put on","r":4,"c":"v. trans.","ec":"put,puts,putting,put,put"}); -const ganul = vEntry({"ts":1527812000,"i":11398,"p":"ګڼل","f":"gaNul, guNul","g":"gaNul,guNul","e":"to count, consider, reckon, suppose, assume","r":4,"c":"v. trans.","tppp":"ګاڼه","tppf":"gaaNu","ec":"deem"}); -const kawulStat = vEntry({"ts":1579015359582,"i":11030,"p":"کول","f":"kawul","g":"kawul","e":"to make ____ ____ (as in \"He's making me angry.\")","r":4,"c":"v. trans.","ssp":"کړ","ssf":"kR","prp":"کړل","prf":"kRul","pprtp":"کړی","pprtf":"kúRey","noOo":true,"ec":"make,makes,making,made,made"}); -const kawulDyn = vEntry({"ts":1527812752,"i":11031,"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 kedulStat = vEntry({"ts":1581086654898,"i":11100,"p":"کېدل","f":"kedul","g":"kedul","e":"to become _____","r":2,"c":"v. intrans.","ssp":"ش","ssf":"sh","prp":"شول","prf":"shwul","pprtp":"شوی","pprtf":"shúwey","noOo":true,"ec":"become"}); -const kedulDyn = vEntry({"ts":1527812754,"i":11101,"p":"کېدل","f":"kedul","g":"kedul","e":"to happen, occur","r":2,"c":"v. intrans.","ssp":"وش","ssf":"óosh","prp":"وشول","prf":"óoshwul","pprtp":"شوی","pprtf":"shúwey","diacExcept":true,"ec":"happen","separationAtP":1,"separationAtF":2}); -const raatlul = vEntry({"ts":1527815216,"i":6875,"p":"راتلل","f":"raatlúl","g":"raatlul","e":"to come","r":4,"c":"v. intrans.","psp":"راځ","psf":"raadz","ssp":"راش","ssf":"ráash","prp":"راغلل","prf":"ráaghlul","pprtp":"راغلی","pprtf":"raaghúley","tppp":"راغی","tppf":"ráaghey","noOo":true,"separationAtP":2,"separationAtF":3,"ec":"come,comes,coming,came,come"}); -const wartlul = vEntry({"ts":1585228579997,"i":14821,"p":"ورتلل","f":"wărtlul","g":"wartlul","e":"to come / go over to (third person or place)","r":4,"c":"v. intrans.","psp":"ورځ","psf":"wărdz","ssp":"ورش","ssf":"wársh","prp":"ورغلل","prf":"wárghlul","pprtp":"ورغلی","pprtf":"wărghúley","tppp":"ورغی","tppf":"wărghey","noOo":true,"separationAtP":2,"separationAtF":3,"ec":"come,comes,coming,came,come"}); -const osedul = vEntry({"ts":1527815139,"i":1127,"p":"اوسېدل","f":"osedul","g":"osedul","e":"to live, reside, stay, be","r":4,"c":"v. intrans.","shortIntrans":true,"diacExcept":true}); -const tlul = vEntry({"ts":1527815348,"i":3791,"p":"تلل","f":"tlul","g":"tlul","e":"to go","r":4,"c":"v. intrans.","psp":"ځ","psf":"dz","ssp":"لاړ ش","ssf":"láaR sh","prp":"لاړ","prf":"láaR","ec":"go,goes,going,went,gone"}); -const awuxtul = vEntry({"ts":1527814012,"i":1133,"p":"اوښتل","f":"awUxtul","g":"awUxtul","e":"to pass over, overturn, be flipped over, spill over, shift, change, diverge, pass, cross, abandon; to be sprained","r":4,"c":"v. intrans.","psp":"اوړ","psf":"awR","ec":"pass","ep":"over"}); -const khorul = vEntry({"ts":1527812790,"i":6002,"p":"خوړل","f":"khoRul","g":"khoRul","e":"to eat, to bite","r":4,"c":"v. trans.","psp":"خور","psf":"khor","tppp":"خوړ","tppf":"khoR","ec":"eat,eats,eating,ate,eaten"}); -const azmoyul = vEntry({"ts":1527811605,"i":468,"p":"ازمویل","f":"azmoyul","g":"azmoyul","e":"to attempt, try; to experiment, test","r":4,"c":"v. trans.","sepOo":true,"ec":"try"}); -const khatul = vEntry({"ts":1527814025,"i":5677,"p":"ختل","f":"khatul","g":"khatul","e":"to climb, ascend, rise, go up; to fall out, to fall off, to leave/dissapear; to turn out to be ...; to give a sentence (in law)","r":3,"c":"v. intrans.","psp":"خېژ","psf":"khejz","tppp":"خوت","tppf":"khot","ec":"climb"}); -const rasedul = vEntry({"ts":1527813573,"i":7057,"p":"رسېدل","f":"rasedul","g":"rasedul","e":"arrive, reach; (fig.) understand, attain to; mature, ripen","r":4,"c":"v. intrans.","shortIntrans":true,"ec":"arrive"}); -const weshul = vEntry({"ts":1527811701,"i":15106,"p":"وېشل","f":"weshul","g":"weshul","e":"divide, distribute, share","r":4,"c":"v. trans.","ec":"divide"}); -const watul = vEntry({"ts":1527823376,"i":14759,"p":"وتل","f":"watul","g":"watul","e":"to go out, exit, leave, emerge","r":4,"c":"v. intrans.","psp":"وځ","psf":"oodz","tppp":"واته","tppf":"waatu","ec":"go,goes,going,went,gone","ep":"out"}); -const wurul = vEntry({"ts":1527816865,"i":14903,"p":"وړل","f":"wuRúl, oRúl, wRul","g":"wuRul,oRul,wRul","e":"to take, carry, bear, move (inanimate objects); to win, earn (subjunctive یوسي - yósee or ویسي - wéesee, simple past یو یې وړلو - yo ye wRulo)","r":3,"c":"v. trans.","ssp":"یوس","ssf":"yos","prp":"یوړل","prf":"yóRul","tppp":"یوړ","tppf":"yoR","noOo":true,"separationAtP":2,"separationAtF":2,"diacExcept":true,"ec":"take,takes,taking,took,taken"}); -const kexodul = vEntry({"ts":1527812284,"i":11113,"p":"کېښودل","f":"kexodul","g":"kexodul","e":"to put, to put down, to set in place","r":4,"c":"v. trans.","psp":"ږد","psf":"Gd","ssp":"کېږد","ssf":"kéGd","noOo":true,"separationAtP":2,"separationAtF":2,"ec":"put,puts,putting,put,put"}); -const kenaastul = vEntry({"ts":1527812759,"i":11124,"p":"کېناستل","f":"kenaastul","g":"kenaastul","e":"to sit down, to have a seat","r":4,"c":"v. intrans.","psp":"کېن","psf":"ken","noOo":true,"separationAtP":2,"separationAtF":2,"ec":"sit,sits,sitting,sat","ep":"down"}); -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 prexodul = vEntry({"ts":1527815190,"i":2495,"p":"پرېښودل","f":"prexodúl","g":"prexodul","e":"to leave, abandon, forsake, let go, allow","r":4,"c":"v. trans.","psp":"پرېږد","psf":"preGd","noOo":true,"separationAtP":3,"separationAtF":3,"ec":"abandon"}); -const raawustul = vEntry({"ts":1527819827,"i":6955,"p":"راوستل","f":"raawustúl","g":"raawustul","e":"to bring, deliver (animate objects), obtain, extract","r":3,"c":"v. trans.","psp":"راول","psf":"raawul","noOo":true,"separationAtP":2,"separationAtF":3,"ec":"bring,brings,bringing,brought,brought"}); +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 achawul = vEntry({ + ts: 1527811872, + i: 224, + p: "اچول", + f: "achawul", + g: "achawul", + e: "to put, pour, drop, throw, put on", + r: 4, + c: "v. trans.", + ec: "put,puts,putting,put,put", +}); +const ganul = vEntry({ + ts: 1527812000, + i: 11398, + p: "ګڼل", + f: "gaNul, guNul", + g: "gaNul,guNul", + e: "to count, consider, reckon, suppose, assume", + r: 4, + c: "v. trans.", + tppp: "ګاڼه", + tppf: "gaaNu", + ec: "deem", +}); +const kawulStat = vEntry({ + ts: 1579015359582, + i: 11030, + p: "کول", + f: "kawul", + g: "kawul", + e: 'to make ____ ____ (as in "He\'s making me angry.")', + r: 4, + c: "v. trans.", + ssp: "کړ", + ssf: "kR", + prp: "کړل", + prf: "kRul", + pprtp: "کړی", + pprtf: "kúRey", + noOo: true, + ec: "make,makes,making,made,made", +}); +const kawulDyn = vEntry({ + ts: 1527812752, + i: 11031, + 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 kedulStat = vEntry({ + ts: 1581086654898, + i: 11100, + p: "کېدل", + f: "kedul", + g: "kedul", + e: "to become _____", + r: 2, + c: "v. intrans.", + ssp: "ش", + ssf: "sh", + prp: "شول", + prf: "shwul", + pprtp: "شوی", + pprtf: "shúwey", + noOo: true, + ec: "become", +}); +const kedulDyn = vEntry({ + ts: 1527812754, + i: 11101, + p: "کېدل", + f: "kedul", + g: "kedul", + e: "to happen, occur", + r: 2, + c: "v. intrans.", + ssp: "وش", + ssf: "óosh", + prp: "وشول", + prf: "óoshwul", + pprtp: "شوی", + pprtf: "shúwey", + diacExcept: true, + ec: "happen", + separationAtP: 1, + separationAtF: 2, +}); +const raatlul = vEntry({ + ts: 1527815216, + i: 6875, + p: "راتلل", + f: "raatlúl", + g: "raatlul", + e: "to come", + r: 4, + c: "v. intrans.", + psp: "راځ", + psf: "raadz", + ssp: "راش", + ssf: "ráash", + prp: "راغلل", + prf: "ráaghlul", + pprtp: "راغلی", + pprtf: "raaghúley", + tppp: "راغی", + tppf: "ráaghey", + noOo: true, + separationAtP: 2, + separationAtF: 3, + ec: "come,comes,coming,came,come", +}); +const wartlul = vEntry({ + ts: 1585228579997, + i: 14821, + p: "ورتلل", + f: "wărtlul", + g: "wartlul", + e: "to come / go over to (third person or place)", + r: 4, + c: "v. intrans.", + psp: "ورځ", + psf: "wărdz", + ssp: "ورش", + ssf: "wársh", + prp: "ورغلل", + prf: "wárghlul", + pprtp: "ورغلی", + pprtf: "wărghúley", + tppp: "ورغی", + tppf: "wărghey", + noOo: true, + separationAtP: 2, + separationAtF: 3, + ec: "come,comes,coming,came,come", +}); +const osedul = vEntry({ + ts: 1527815139, + i: 1127, + p: "اوسېدل", + f: "osedul", + g: "osedul", + e: "to live, reside, stay, be", + r: 4, + c: "v. intrans.", + shortIntrans: true, + diacExcept: true, +}); +const awuxtul = vEntry({ + ts: 1527814012, + i: 1133, + p: "اوښتل", + f: "awUxtul", + g: "awUxtul", + e: "to pass over, overturn, be flipped over, spill over, shift, change, diverge, pass, cross, abandon; to be sprained", + r: 4, + c: "v. intrans.", + psp: "اوړ", + psf: "awR", + ec: "pass", + ep: "over", +}); +const khorul = vEntry({ + ts: 1527812790, + i: 6002, + p: "خوړل", + f: "khoRul", + g: "khoRul", + e: "to eat, to bite", + r: 4, + c: "v. trans.", + psp: "خور", + psf: "khor", + tppp: "خوړ", + tppf: "khoR", + ec: "eat,eats,eating,ate,eaten", +}); +const azmoyul = vEntry({ + ts: 1527811605, + i: 468, + p: "ازمویل", + f: "azmoyul", + g: "azmoyul", + e: "to attempt, try; to experiment, test", + r: 4, + c: "v. trans.", + sepOo: true, + ec: "try", +}); +const khatul = vEntry({ + ts: 1527814025, + i: 5677, + p: "ختل", + f: "khatul", + g: "khatul", + e: "to climb, ascend, rise, go up; to fall out, to fall off, to leave/dissapear; to turn out to be ...; to give a sentence (in law)", + r: 3, + c: "v. intrans.", + psp: "خېژ", + psf: "khejz", + tppp: "خوت", + tppf: "khot", + ec: "climb", +}); +const rasedul = vEntry({ + ts: 1527813573, + i: 7057, + p: "رسېدل", + f: "rasedul", + g: "rasedul", + e: "arrive, reach; (fig.) understand, attain to; mature, ripen", + r: 4, + c: "v. intrans.", + shortIntrans: true, + ec: "arrive", +}); +const weshul = vEntry({ + ts: 1527811701, + i: 15106, + p: "وېشل", + f: "weshul", + g: "weshul", + e: "divide, distribute, share", + r: 4, + c: "v. trans.", + ec: "divide", +}); +const watul = vEntry({ + ts: 1527823376, + i: 14759, + p: "وتل", + f: "watul", + g: "watul", + e: "to go out, exit, leave, emerge", + r: 4, + c: "v. intrans.", + psp: "وځ", + psf: "oodz", + tppp: "واته", + tppf: "waatu", + ec: "go,goes,going,went,gone", + ep: "out", +}); +const wurul = vEntry({ + ts: 1527816865, + i: 14903, + p: "وړل", + f: "wuRúl, oRúl, wRul", + g: "wuRul,oRul,wRul", + e: "to take, carry, bear, move (inanimate objects); to win, earn (subjunctive یوسي - yósee or ویسي - wéesee, simple past یو یې وړلو - yo ye wRulo)", + r: 3, + c: "v. trans.", + ssp: "یوس", + ssf: "yos", + prp: "یوړل", + prf: "yóRul", + tppp: "یوړ", + tppf: "yoR", + noOo: true, + separationAtP: 2, + separationAtF: 2, + diacExcept: true, + ec: "take,takes,taking,took,taken", +}); +const kexodul = vEntry({ + ts: 1527812284, + i: 11113, + p: "کېښودل", + f: "kexodul", + g: "kexodul", + e: "to put, to put down, to set in place", + r: 4, + c: "v. trans.", + psp: "ږد", + psf: "Gd", + ssp: "کېږد", + ssf: "kéGd", + noOo: true, + separationAtP: 2, + separationAtF: 2, + ec: "put,puts,putting,put,put", +}); +const kenaastul = vEntry({ + ts: 1527812759, + i: 11124, + p: "کېناستل", + f: "kenaastul", + g: "kenaastul", + e: "to sit down, to have a seat", + r: 4, + c: "v. intrans.", + psp: "کېن", + psf: "ken", + noOo: true, + separationAtP: 2, + separationAtF: 2, + ec: "sit,sits,sitting,sat", + ep: "down", +}); +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 prexodul = vEntry({ + ts: 1527815190, + i: 2495, + p: "پرېښودل", + f: "prexodúl", + g: "prexodul", + e: "to leave, abandon, forsake, let go, allow", + r: 4, + c: "v. trans.", + psp: "پرېږد", + psf: "preGd", + noOo: true, + separationAtP: 3, + separationAtF: 3, + ec: "abandon", +}); +const raawustul = vEntry({ + ts: 1527819827, + i: 6955, + p: "راوستل", + f: "raawustúl", + g: "raawustul", + e: "to bring, deliver (animate objects), obtain, extract", + r: 3, + c: "v. trans.", + psp: "راول", + psf: "raawul", + noOo: true, + separationAtP: 2, + separationAtF: 3, + ec: "bring,brings,bringing,brought,brought", +}); +const tlul = vEntry({ + ts: 1527815348, + i: 3804, + p: "تلل", + f: "tlul", + g: "tlul", + e: "to go", + r: 4, + c: "v. intrans.", + psp: "ځ", + psf: "dz", + ssp: "لاړ ش", + ssf: "láaR sh", + prp: "لاړ", + prf: "láaR", + ec: "go,goes,going,went,gone", +}); 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."}, + { + 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."}, + { + 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 stureyKawul = vEntry( - {"ts":1591033078746,"i":7877,"p":"ستړی کول","f":"stuRey kawul","g":"stuReykawul","e":"to make tired, wear out","r":4,"c":"v. stat. comp. trans.","l":1527815306,"ec":"make","ep":"tired"}, - {"ts":1527815306,"i":7876,"p":"ستړی","f":"stúRey","g":"stuRey","e":"tired","r":4,"c":"adj. / adv."}, + { + ts: 1591033078746, + i: 7877, + p: "ستړی کول", + f: "stuRey kawul", + g: "stuReykawul", + e: "to make tired, wear out", + r: 4, + c: "v. stat. comp. trans.", + l: 1527815306, + ec: "make", + ep: "tired", + }, + { + ts: 1527815306, + i: 7876, + p: "ستړی", + f: "stúRey", + g: "stuRey", + e: "tired", + r: 4, + c: "adj. / adv.", + } ); const stureyKedul = vEntry( - {"ts":1591033069786,"i":7878,"p":"ستړی کېدل","f":"stuRey kedul","g":"stuReykedul","e":"to get tired, fatigued","r":4,"c":"v. stat. comp. intrans.","l":1527815306,"ec":"get","ep":"tired"}, - {"ts":1527815306,"i":7876,"p":"ستړی","f":"stúRey","g":"stuRey","e":"tired","r":4,"c":"adj. / adv."}, + { + ts: 1591033069786, + i: 7878, + p: "ستړی کېدل", + f: "stuRey kedul", + g: "stuReykedul", + e: "to get tired, fatigued", + r: 4, + c: "v. stat. comp. intrans.", + l: 1527815306, + ec: "get", + ep: "tired", + }, + { + ts: 1527815306, + i: 7876, + p: "ستړی", + f: "stúRey", + g: "stuRey", + e: "tired", + r: 4, + c: "adj. / adv.", + } ); const bayaanedul = vEntry( - {"ts":1659037345120,"i":2055,"p":"بیانېدل","f":"bayaanedúl","g":"bayaanedul","e":"to be described, told, narrated, explained, declared","r":4,"c":"v. stat. comp. intrans.","l":1527814259}, - {"ts":1527814259,"i":2052,"p":"بیان","f":"bayáan","g":"bayaan","e":"description, statement, speaking, narration, sermon","r":4,"c":"n. m.","app":"بیانات","apf":"bayaanaat"}, + { + ts: 1659037345120, + i: 2055, + p: "بیانېدل", + f: "bayaanedúl", + g: "bayaanedul", + e: "to be described, told, narrated, explained, declared", + r: 4, + c: "v. stat. comp. intrans.", + l: 1527814259, + }, + { + ts: 1527814259, + i: 2052, + p: "بیان", + f: "bayáan", + g: "bayaan", + e: "description, statement, speaking, narration, sermon", + r: 4, + c: "n. m.", + app: "بیانات", + apf: "bayaanaat", + } ); const khufaKedul = vEntry( - {"ts":1577898920635,"i":5845,"p":"خفه کېدل","f":"khufa kedul","g":"khufakedul","e":"to become sad, grieved, annoyed, upset; to be choked, to suffocate","r":4,"c":"v. stat. comp. intrans.","l":1527812798,"ec":"become","ep":"sad"}, - {"ts":1527812798,"i":5843,"p":"خفه","f":"khufa","g":"khufa","e":"sad, upset, angry; choked, suffocated","r":4,"c":"adj."}, + { + ts: 1577898920635, + i: 5845, + p: "خفه کېدل", + f: "khufa kedul", + g: "khufakedul", + e: "to become sad, grieved, annoyed, upset; to be choked, to suffocate", + r: 4, + c: "v. stat. comp. intrans.", + l: 1527812798, + ec: "become", + ep: "sad", + }, + { + ts: 1527812798, + i: 5843, + p: "خفه", + f: "khufa", + g: "khufa", + e: "sad, upset, angry; choked, suffocated", + r: 4, + c: "adj.", + } ); -const ooPH: T.PH= { type: "PH", ps: { p: "و", f: "óo" }}; +const ooPH: T.PH = { type: "PH", ps: { p: "و", f: "óo" } }; describe("imperfective stems", () => { const tests: { - title: string, + title: string; tests: { - verb: T.VerbEntry, - genderNumber?: T.GenderNumber, - result: T.RootsStemsOutput, - }[], + verb: T.VerbEntry; + genderNumber?: T.GenderNumber; + result: T.RootsStemsOutput; + }[]; }[] = [ { title: "is the shortened infinitive for regular verbs", tests: [ { verb: ganul, - result: [[], [{ type: "VB", ps: [{ p: "ګڼ", f: "gaN" }]}]], + result: [[], [{ type: "VB", ps: [{ p: "ګڼ", f: "gaN" }] }]], }, { verb: wahul, - result: [[], [{ type: "VB", ps: [{ p: "وه", f: "wah" }]}]], + result: [[], [{ type: "VB", ps: [{ p: "وه", f: "wah" }] }]], }, ], }, @@ -98,31 +582,41 @@ describe("imperfective stems", () => { tests: [ { verb: ghadzedul, - result: [[], [{ type: "VB", ps: [{ p: "غځېږ", f: "ghadzéG" }]}]], + result: [[], [{ type: "VB", ps: [{ p: "غځېږ", f: "ghadzéG" }] }]], }, { verb: rasedul, - result: [[], [{ - type: "VB", - ps: { - long: [{ p: "رسېږ", f: "raséG" }], - short: [{ p: "رس", f: "ras" }], - }, - }]], + result: [ + [], + [ + { + type: "VB", + ps: { + long: [{ p: "رسېږ", f: "raséG" }], + short: [{ p: "رس", f: "ras" }], + }, + }, + ], + ], }, ], }, { - title: "is the special imperfective stem for irregular verbs with a special imperfective stem", + title: + "is the special imperfective stem for irregular verbs with a special imperfective stem", tests: [ { verb: khorul, - result: [[], [{ type: "VB", ps: [{ p: "خور", f: "khor" }]}]], + result: [[], [{ type: "VB", ps: [{ p: "خور", f: "khor" }] }]], }, { verb: kexodul, result: [[], [{ type: "VB", ps: [{ p: "ږد", f: "Gd" }] }]], }, + { + verb: tlul, + result: [[], [{ type: "VB", ps: [{ p: "ځ", f: "dz" }] }]], + }, ], }, { @@ -131,101 +625,134 @@ describe("imperfective stems", () => { { verb: bandawul, genderNumber: { gender: "masc", number: "singular" }, - result: [[], [{ type: "VB", ps: [{ p: "بندو", f: "bandawX" }]}]], + result: [[], [{ type: "VB", ps: [{ p: "بندو", f: "bandawX" }] }]], }, { verb: bandedul, genderNumber: { gender: "masc", number: "singular" }, - result: [[], [{ type: "VB", ps: [{ p: "بندېږ", f: "bandéG" }]}]], + result: [[], [{ type: "VB", ps: [{ p: "بندېږ", f: "bandéG" }] }]], }, ], }, { - title: "is welded together with the complement on seperated stative compounds", + title: + "is welded together with the complement on seperated stative compounds", tests: [ { verb: stureyKawul, genderNumber: { gender: "fem", number: "singular" }, - result: [[], [{ - type: "welded", - left: { - type: "NComp", - comp: { - type: "AdjComp", - ps: { p: "ستړې", f: "stuRe" }, - number: "singular", - gender: "fem", + result: [ + [], + [ + { + type: "welded", + left: { + type: "NComp", + comp: { + type: "AdjComp", + ps: { p: "ستړې", f: "stuRe" }, + number: "singular", + gender: "fem", + }, + }, + right: { + type: "VB", + ps: [{ p: "کو", f: "kawX" }], + }, }, - }, - right: { - type: "VB", - ps: [{ p: "کو", f: "kawX" }], - }, - }]], + ], + ], }, { verb: stureyKedul, genderNumber: { gender: "fem", number: "singular" }, - result: [[], [{ - type: "welded", - left: { - type: "NComp", - comp: { - type: "AdjComp", - ps: { p: "ستړې", f: "stuRe" }, - number: "singular", - gender: "fem", + result: [ + [], + [ + { + type: "welded", + left: { + type: "NComp", + comp: { + type: "AdjComp", + ps: { p: "ستړې", f: "stuRe" }, + number: "singular", + gender: "fem", + }, + }, + right: { + type: "VB", + ps: [{ p: "کېږ", f: "kéG" }], + }, }, - }, - right: { - type: "VB", - ps: [{ p: "کېږ", f: "kéG" }], - }, - }]], + ], + ], }, - ] - } + ], + }, ]; - tests.forEach(x => { + tests.forEach((x) => { test(x.title, () => { - x.tests.forEach(y => { - expect(getRootStem({ - verb: y.verb, - aspect: "imperfective", - type: "basic", - rs: "stem", - voice: "active", - genderNumber: y.genderNumber || { - gender: "masc", - number: "singular", - }, - })).toEqual(y.result); + x.tests.forEach((y) => { + expect( + getRootStem({ + verb: y.verb, + aspect: "imperfective", + type: "basic", + rs: "stem", + voice: "active", + genderNumber: y.genderNumber || { + gender: "masc", + number: "singular", + }, + }) + ).toEqual(y.result); }); - }) + }); }); }); describe("imperfective roots", () => { const tests: { - title: string, + title: string; tests: { - verb: T.VerbEntry, - genderNumber?: T.GenderNumber, - result: T.RootsStemsOutput, - }[], + verb: T.VerbEntry; + genderNumber?: T.GenderNumber; + result: T.RootsStemsOutput; + }[]; }[] = [ { - title: "is the infinitive with and without ل for regular verbs, with a trailing accent on the short form", + title: + "is the infinitive with and without ل for regular verbs, with a trailing accent on the short form", tests: [ { verb: wahul, - result: [[], [{ - type: "VB", - ps: { - long: [{ p: "وهل", f: "wahúl" }], - short: [{ p: "وه", f: "wahX" }], + result: [ + [], + [ + { + type: "VB", + ps: { + long: [{ p: "وهل", f: "wahúl" }], + short: [{ p: "وه", f: "wahX" }], + }, }, - }] + ], + ], + }, + { + verb: tlul, + result: [ + [], + [ + { + type: "VB", + ps: { + long: [{ p: "تلل", f: "tlúl" }], + short: [{ p: "تل", f: "tlX" }], + }, + }, + ], ], }, ], @@ -239,12 +766,12 @@ describe("imperfective roots", () => { result: [ [], [ - { + { type: "VB", ps: { long: [{ p: "بندول", f: "bandawúl" }], short: [{ p: "بندو", f: "bandawX" }], - } + }, }, ], ], @@ -259,7 +786,7 @@ describe("imperfective roots", () => { type: "VB", ps: { long: [{ p: "بندېدل", f: "bandedúl" }], - short: [{ p: "بندېد", f: "bandedX" }] + short: [{ p: "بندېد", f: "bandedX" }], }, }, ], @@ -268,98 +795,108 @@ describe("imperfective roots", () => { ], }, { - title: "is welded together with the complement on seperated stative compounds", + title: + "is welded together with the complement on seperated stative compounds", tests: [ { verb: stureyKawul, genderNumber: { gender: "fem", number: "singular" }, - result: [[], [{ - type: "welded", - left: { - type: "NComp", - comp: { - type: "AdjComp", - ps: { p: "ستړې", f: "stuRe" }, - number: "singular", - gender: "fem", + result: [ + [], + [ + { + type: "welded", + left: { + type: "NComp", + comp: { + type: "AdjComp", + ps: { p: "ستړې", f: "stuRe" }, + number: "singular", + gender: "fem", + }, + }, + right: { + type: "VB", + ps: { + long: [{ p: "کول", f: "kawúl" }], + short: [{ p: "کو", f: "kawX" }], + }, + }, }, - }, - right: { - type: "VB", - ps: { - long: [{ p: "کول", f: "kawúl" }], - short: [{ p: "کو", f: "kawX" }], - }, - }, - }]], + ], + ], }, { verb: stureyKedul, genderNumber: { gender: "fem", number: "singular" }, - result: [[], [{ - type: "welded", - left: { - type: "NComp", - comp: { - type: "AdjComp", - ps: { p: "ستړې", f: "stuRe" }, - number: "singular", - gender: "fem", + result: [ + [], + [ + { + type: "welded", + left: { + type: "NComp", + comp: { + type: "AdjComp", + ps: { p: "ستړې", f: "stuRe" }, + number: "singular", + gender: "fem", + }, + }, + right: { + type: "VB", + ps: { + long: [{ p: "کېدل", f: "kedúl" }], + short: [{ p: "کېد", f: "kedX" }], + }, + }, }, - }, - right: { - type: "VB", - ps: { - long: [{ p: "کېدل", f: "kedúl" }], - short: [{ p: "کېد", f: "kedX" }], - }, - }, - }]], + ], + ], }, - ] - } + ], + }, ]; - tests.forEach(x => { + tests.forEach((x) => { test(x.title, () => { - x.tests.forEach(y => { - expect(getRootStem({ - verb: y.verb, - aspect: "imperfective", - type: "basic", - rs: "root", - voice: "active", - genderNumber: y.genderNumber || { - gender: "masc", - number: "singular", - }, - })).toEqual(y.result); + x.tests.forEach((y) => { + expect( + getRootStem({ + verb: y.verb, + aspect: "imperfective", + type: "basic", + rs: "root", + voice: "active", + genderNumber: y.genderNumber || { + gender: "masc", + number: "singular", + }, + }) + ).toEqual(y.result); }); - }) + }); }); }); describe("perfective stems", () => { const tests: { - title: string, + title: string; tests: { - verb: T.VerbEntry, - genderNumber?: T.GenderNumber, - result: T.RootsStemsOutput, - }[], + verb: T.VerbEntry; + genderNumber?: T.GenderNumber; + result: T.RootsStemsOutput; + }[]; }[] = [ { title: "is the imprefective stem with an oo- prefix for regular verbs", tests: [ { verb: ganul, - result: [ - [ooPH], - [{ type: "VB", ps: [{ p: "ګڼ", f: "gaN" }]}], - ], + result: [[ooPH], [{ type: "VB", ps: [{ p: "ګڼ", f: "gaN" }] }]], }, { verb: wahul, - result: [[ooPH], [{ type: "VB", ps: [{ p: "وه", f: "wah" }]}]], + result: [[ooPH], [{ type: "VB", ps: [{ p: "وه", f: "wah" }] }]], }, ], }, @@ -368,7 +905,7 @@ describe("perfective stems", () => { tests: [ { verb: ghadzedul, - result: [[ooPH], [{ type: "VB", ps: [{ p: "غځېږ", f: "ghadzeG" }]}]], + result: [[ooPH], [{ type: "VB", ps: [{ p: "غځېږ", f: "ghadzeG" }] }]], }, { verb: rasedul, @@ -388,11 +925,12 @@ describe("perfective stems", () => { ], }, { - title: "the imperfective stem the perfective stem is regularly built off could be irregular", + title: + "the imperfective stem the perfective stem is regularly built off could be irregular", tests: [ { verb: khorul, - result: [[ooPH], [{ type: "VB", ps: [{ p: "خور", f: "khor" }]}]], + result: [[ooPH], [{ type: "VB", ps: [{ p: "خور", f: "khor" }] }]], }, ], }, @@ -402,11 +940,11 @@ describe("perfective stems", () => { { verb: wurul, result: [ - [{ type: "PH", ps: { p: "یو", f: "yó" }}], - [{ type: "VB", ps: [{ p: "س", f: "s" }]}], + [{ type: "PH", ps: { p: "یو", f: "yó" } }], + [{ type: "VB", ps: [{ p: "س", f: "s" }] }], ], - } - ] + }, + ], }, { title: "includes extra irregular short roots for kawul verbs", @@ -423,7 +961,7 @@ describe("perfective stems", () => { short: [{ p: "ک", f: "k" }], }, }, - ] + ], ], }, ], @@ -443,7 +981,7 @@ describe("perfective stems", () => { short: [{ p: "ک", f: "k" }], }, }, - ] + ], ], }, ], @@ -475,30 +1013,24 @@ describe("perfective stems", () => { verb: tlul, genderNumber: { gender: "fem", number: "singular" }, result: [ - [{ type: "PH", ps: { p: "لاړه ", f: "láaRa " }}], - [ - { type: "VB", ps: [{ p: "ش", f: "sh" }]}, - ], + [{ type: "PH", ps: { p: "لاړه ", f: "láaRa " } }], + [{ type: "VB", ps: [{ p: "ش", f: "sh" }] }], ], }, { verb: tlul, genderNumber: { gender: "fem", number: "plural" }, result: [ - [{ type: "PH", ps: { p: "لاړې ", f: "láaRe " }}], - [ - { type: "VB", ps: [{ p: "ش", f: "sh" }]}, - ], + [{ type: "PH", ps: { p: "لاړې ", f: "láaRe " } }], + [{ type: "VB", ps: [{ p: "ش", f: "sh" }] }], ], }, { verb: tlul, genderNumber: { gender: "masc", number: "plural" }, result: [ - [{ type: "PH", ps: { p: "لاړ ", f: "láaR " }}], - [ - { type: "VB", ps: [{ p: "ش", f: "sh" }]}, - ], + [{ type: "PH", ps: { p: "لاړ ", f: "láaR " } }], + [{ type: "VB", ps: [{ p: "ش", f: "sh" }] }], ], }, ], @@ -633,9 +1165,7 @@ describe("perfective stems", () => { verb: azmoyul, genderNumber: { gender: "masc", number: "plural" }, result: [ - [ - { type: "PH", ps: { p: "و ", f: "óo`" }}, - ], + [{ type: "PH", ps: { p: "و ", f: "óo`" } }], [ { type: "VB", @@ -648,9 +1178,7 @@ describe("perfective stems", () => { verb: achawul, genderNumber: { gender: "masc", number: "plural" }, result: [ - [ - { type: "PH", ps: { p: "وا", f: "wáa" }}, - ], + [{ type: "PH", ps: { p: "وا", f: "wáa" } }], [ { type: "VB", @@ -663,21 +1191,15 @@ describe("perfective stems", () => { verb: watul, genderNumber: { gender: "masc", number: "plural" }, result: [ - [ - { type: "PH", ps: { p: "و", f: "wÚ" }}, - ], - [ - { type: "VB", ps: [{ p: "وځ", f: "oodz" }]}, - ], + [{ type: "PH", ps: { p: "و", f: "wÚ" } }], + [{ type: "VB", ps: [{ p: "وځ", f: "oodz" }] }], ], }, { verb: osedul, genderNumber: { gender: "masc", number: "plural" }, result: [ - [ - { type: "PH", ps: { p: "و", f: "óo`" }}, - ], + [{ type: "PH", ps: { p: "و", f: "óo`" } }], [ { type: "VB", @@ -692,33 +1214,35 @@ describe("perfective stems", () => { ], }, ]; - tests.forEach(x => { + tests.forEach((x) => { test(x.title, () => { - x.tests.forEach(y => { - expect(getRootStem({ - verb: y.verb, - aspect: "perfective", - type: "basic", - rs: "stem", - voice: "active", - genderNumber: y.genderNumber || { - gender: "masc", - number: "singular", - }, - })).toEqual(y.result); + x.tests.forEach((y) => { + expect( + getRootStem({ + verb: y.verb, + aspect: "perfective", + type: "basic", + rs: "stem", + voice: "active", + genderNumber: y.genderNumber || { + gender: "masc", + number: "singular", + }, + }) + ).toEqual(y.result); }); - }) + }); }); }); describe("perfective roots", () => { const tests: { - title: string, + title: string; tests: { - verb: T.VerbEntry, - genderNumber?: T.GenderNumber, - result: T.RootsStemsOutput, - }[], + verb: T.VerbEntry; + genderNumber?: T.GenderNumber; + result: T.RootsStemsOutput; + }[]; }[] = [ { title: "is the imprefective root with an oo- prefix for regular verbs", @@ -743,7 +1267,7 @@ describe("perfective roots", () => { result: [ [ooPH], [ - { + { type: "VB", ps: { long: [{ p: "وهل", f: "wahul" }], @@ -761,14 +1285,34 @@ describe("perfective roots", () => { { verb: wurul, result: [ - [{ type: "PH", ps: { p: "یو", f: "yó" }}], + [{ type: "PH", ps: { p: "یو", f: "yó" } }], [ { type: "VB", ps: { long: [{ p: "ړل", f: "Rul" }], short: [{ p: "ړ", f: "R" }], - } + }, + }, + ], + ], + }, + ], + }, + { + title: "is the special form with tlul", + tests: [ + { + verb: tlul, + result: [ + [{ type: "PH", ps: { p: "لا", f: "láa" } }], + [ + { + type: "VB", + ps: { + long: [{ p: "ړل", f: "Rul" }], + short: [{ p: "ړ", f: "R" }], + }, }, ], ], @@ -812,7 +1356,7 @@ describe("perfective roots", () => { mini: [{ p: "ک", f: "k" }], }, }, - ] + ], ], }, ], @@ -889,7 +1433,7 @@ describe("perfective roots", () => { ps: { long: [{ p: "شول", f: "shwul" }], short: [{ p: "شو", f: "shw" }], - } + }, }, ], ], @@ -915,7 +1459,7 @@ describe("perfective roots", () => { ps: { long: [{ p: "شول", f: "shwul" }], short: [{ p: "شو", f: "shw" }], - } + }, }, ], ], @@ -923,159 +1467,279 @@ describe("perfective roots", () => { ], }, ]; - tests.forEach(x => { + tests.forEach((x) => { test(x.title, () => { - x.tests.forEach(y => { - expect(getRootStem({ - verb: y.verb, - aspect: "perfective", - type: "basic", - rs: "root", - voice: "active", - genderNumber: y.genderNumber || { - gender: "masc", - number: "singular", - }, - })).toEqual(y.result); + x.tests.forEach((y) => { + expect( + getRootStem({ + verb: y.verb, + aspect: "perfective", + type: "basic", + rs: "root", + voice: "active", + genderNumber: y.genderNumber || { + gender: "masc", + number: "singular", + }, + }) + ).toEqual(y.result); }); - }) + }); }); }); describe("past participles", () => { test("for most verbs are just the imperfective root (imperative) plus ی - ey", () => { - expect(getPastParticiple(rasedul, "active", { gender: "masc", number: "singular" })) - .toEqual({ - type: "VB", - ps: [{ p: "رسېدلی", f: "rasedúley" }], + expect( + getPastParticiple(rasedul, "active", { gender: "masc", number: "singular", - }); - expect(getPastParticiple(ganul, "active", { gender: "fem", number: "singular" })) - .toEqual({ - type: "VB", - ps: [{ p: "ګڼلې", f: "gaNúle" }], - gender: "fem", - number: "singular", - }); + }) + ).toEqual({ + type: "VB", + ps: [{ p: "رسېدلی", f: "rasedúley" }], + gender: "masc", + number: "singular", + }); + expect( + getPastParticiple(ganul, "active", { gender: "fem", number: "singular" }) + ).toEqual({ + type: "VB", + ps: [{ p: "ګڼلې", f: "gaNúle" }], + gender: "fem", + number: "singular", + }); }); test("for verbs like اېښودل and پرېښودل they have a short version shortened taking off after ښ", () => { - expect(getPastParticiple(prexodul, "active", { gender: "masc", number: "plural" })) - .toEqual({ - type: "VB", - ps: { - long: [{ p: "پرېښودلي", f: "prexodúlee" }], - short: [{ p: "پرېښي", f: "préxee" }], - }, + expect( + getPastParticiple(prexodul, "active", { gender: "masc", number: "plural", - }); + }) + ).toEqual({ + type: "VB", + ps: { + long: [{ p: "پرېښودلي", f: "prexodúlee" }], + short: [{ p: "پرېښي", f: "préxee" }], + }, + gender: "masc", + number: "plural", + }); }); test("verbs ending in ستل ښتل وتل or وړل verbs also have a short version", () => { - expect(getPastParticiple(raawustul, "active", { gender: "fem", number: "plural" })) - .toEqual({ - type: "VB", - ps: { - long: [{ p: "راوستلې", f: "raawustúle" }], - short: [{ p: "راوستې", f: "raawúste" }], - }, + expect( + getPastParticiple(raawustul, "active", { gender: "fem", number: "plural", - }); - expect(getPastParticiple(awuxtul, "active", { gender: "masc", number: "plural" })) - .toEqual({ - type: "VB", - ps: { - long: [{ p: "اوښتلي", f: "awUxtúlee" }], - short: [{ p: "اوښتي", f: "awÚxtee" }], - }, - gender: "masc", - number: "plural", - }); - expect(getPastParticiple(watul, "active", { gender: "fem", number: "singular" })) - .toEqual({ - type: "VB", - ps: { - long: [{ p: "وتلې", f: "watúle" }], - short: [{ p: "وتې", f: "wáte" }], - }, - gender: "fem", - number: "singular", - }); + }) + ).toEqual({ + type: "VB", + ps: { + long: [{ p: "راوستلې", f: "raawustúle" }], + short: [{ p: "راوستې", f: "raawúste" }], + }, + gender: "fem", + number: "plural", + }); + expect( + getPastParticiple(awuxtul, "active", { gender: "masc", number: "plural" }) + ).toEqual({ + type: "VB", + ps: { + long: [{ p: "اوښتلي", f: "awUxtúlee" }], + short: [{ p: "اوښتي", f: "awÚxtee" }], + }, + gender: "masc", + number: "plural", + }); + expect( + getPastParticiple(watul, "active", { gender: "fem", number: "singular" }) + ).toEqual({ + type: "VB", + ps: { + long: [{ p: "وتلې", f: "watúle" }], + short: [{ p: "وتې", f: "wáte" }], + }, + gender: "fem", + number: "singular", + }); }); test("but not verbs ending with استل", () => { - expect(getPastParticiple(kenaastul, "active", { gender: "fem", number: "plural" })) - .toEqual({ - type: "VB", - ps: [{ p: "کېناستلې", f: "kenaastúle" }], + expect( + getPastParticiple(kenaastul, "active", { gender: "fem", number: "plural", - }); + }) + ).toEqual({ + type: "VB", + ps: [{ p: "کېناستلې", f: "kenaastúle" }], + gender: "fem", + number: "plural", + }); }); test("special short form with تلل - tlul", () => { - expect(getPastParticiple(tlul, "active", { gender: "masc", number: "plural" })) - .toEqual({ - type: "VB", - ps: { - long: [{ p: "تللي", f: "tlúlee" }], - short: [{ p: "تلي", f: "túlee" }], - }, - gender: "masc", - number: "plural", - }); + expect( + getPastParticiple(tlul, "active", { gender: "masc", number: "plural" }) + ).toEqual({ + type: "VB", + ps: { + long: [{ p: "تللي", f: "tlúlee" }], + short: [{ p: "تلي", f: "túlee" }], + }, + gender: "masc", + number: "plural", + }); }); test("kawul/kedul/raatlul verbs have an irregular pprt fields that give us the irregular past participle", () => { - expect(getPastParticiple(kawulDyn, "active", { gender: "masc", number: "singular" })) - .toEqual({ - type: "VB", - ps: [{ p: "کړی", f: "kúRey" }], + expect( + getPastParticiple(kawulDyn, "active", { gender: "masc", number: "singular", - }); - expect(getPastParticiple(kawulStat, "active", { gender: "masc", number: "plural" })) - .toEqual({ - type: "VB", - ps: [{ p: "کړي", f: "kúRee" }], + }) + ).toEqual({ + type: "VB", + ps: [{ p: "کړی", f: "kúRey" }], + gender: "masc", + number: "singular", + }); + expect( + getPastParticiple(kawulStat, "active", { gender: "masc", number: "plural", - }); - expect(getPastParticiple(kedulStat, "active", { gender: "fem", number: "singular" })) - .toEqual({ - type: "VB", - ps: [{ p: "شوې", f: "shúwe" }], + }) + ).toEqual({ + type: "VB", + ps: [{ p: "کړي", f: "kúRee" }], + gender: "masc", + number: "plural", + }); + expect( + getPastParticiple(kedulStat, "active", { gender: "fem", number: "singular", - }); - expect(getPastParticiple(kedulDyn, "active", { gender: "fem", number: "plural" })) - .toEqual({ - type: "VB", - ps: [{ p: "شوې", f: "shúwe" }], - gender: "fem", - number: "plural", - }); + }) + ).toEqual({ + type: "VB", + ps: [{ p: "شوې", f: "shúwe" }], + gender: "fem", + number: "singular", + }); + expect( + getPastParticiple(kedulDyn, "active", { gender: "fem", number: "plural" }) + ).toEqual({ + type: "VB", + ps: [{ p: "شوې", f: "shúwe" }], + gender: "fem", + number: "plural", + }); }); test("stative compounds weld the complement to the kawul/kedul participle", () => { - expect(getPastParticiple(bandawul, "active", { gender: "fem", number: "singular" })) - .toEqual({ - type: "welded", - left: { - type: "NComp", - comp: { - type: "AdjComp", - ps: { p: "بنده", f: "banda" }, - gender: "fem", - number: "singular", - }, - }, - right: { - type: "VB", - ps: [{ p: "کړې", f: "kúRe" }], + expect( + getPastParticiple(bandawul, "active", { + gender: "fem", + number: "singular", + }) + ).toEqual({ + type: "welded", + left: { + type: "NComp", + comp: { + type: "AdjComp", + ps: { p: "بنده", f: "banda" }, gender: "fem", number: "singular", }, - }); - expect(getPastParticiple(bandedul, "active", { gender: "fem", number: "plural" })) - .toEqual({ + }, + right: { + type: "VB", + ps: [{ p: "کړې", f: "kúRe" }], + gender: "fem", + number: "singular", + }, + }); + expect( + getPastParticiple(bandedul, "active", { gender: "fem", number: "plural" }) + ).toEqual({ + type: "welded", + left: { + type: "NComp", + comp: { + type: "AdjComp", + ps: { p: "بندې", f: "bande" }, + gender: "fem", + number: "plural", + }, + }, + right: { + type: "VB", + ps: [{ p: "شوې", f: "shúwe" }], + gender: "fem", + number: "plural", + }, + }); + }); + test("for passive with simple verbs, long perfective root welded to kedul participle", () => { + expect( + getPastParticiple(ganul, "passive", { gender: "fem", number: "singular" }) + ).toEqual({ + type: "welded", + left: { + type: "VB", + ps: [{ p: "ګڼل", f: "gaNul" }], + }, + right: { + type: "VB", + ps: [{ p: "شوې", f: "shúwe" }], + gender: "fem", + number: "singular", + }, + }); + }); + test("special passive forms for kawul verbs - kRul perfective root + shúwey", () => { + expect( + getPastParticiple(kawulStat, "passive", { + gender: "masc", + number: "singular", + }) + ).toEqual({ + type: "welded", + left: { + type: "VB", + ps: [{ p: "کړل", f: "kRul" }], + }, + right: { + type: "VB", + ps: [{ p: "شوی", f: "shúwey" }], + gender: "masc", + number: "singular", + }, + }); + expect( + getPastParticiple(kawulDyn, "passive", { + gender: "masc", + number: "singular", + }) + ).toEqual({ + type: "welded", + left: { + type: "VB", + ps: [{ p: "کړل", f: "kRul" }], + }, + right: { + type: "VB", + ps: [{ p: "شوی", f: "shúwey" }], + gender: "masc", + number: "singular", + }, + }); + expect( + getPastParticiple(bandawul, "passive", { + gender: "fem", + number: "plural", + }) + ).toEqual({ + type: "welded", + left: { type: "welded", left: { type: "NComp", @@ -1087,101 +1751,35 @@ describe("past participles", () => { }, }, right: { - type: "VB", - ps: [{ p: "شوې", f: "shúwe" }], - gender: "fem", - number: "plural", - }, - }); - }); - test("for passive with simple verbs, long perfective root welded to kedul participle", () => { - expect(getPastParticiple(ganul, "passive", { gender: "fem", number: "singular" })) - .toEqual({ - type: "welded", - left: { - type: "VB", - ps: [{ p: "ګڼل", f: "gaNul" }], - }, - right: { - type: "VB", - ps: [{ p: "شوې", f: "shúwe" }], - gender: "fem", - number: "singular", - }, - }); - }); - test("special passive forms for kawul verbs - kRul perfective root + shúwey", () => { - expect(getPastParticiple(kawulStat, "passive", { gender: "masc", number: "singular"})) - .toEqual({ - type: "welded", - left: { type: "VB", ps: [{ p: "کړل", f: "kRul" }], }, - right: { - type: "VB", - ps: [{ p: "شوی", f: "shúwey" }], - gender: "masc", - number: "singular", - }, - }); - expect(getPastParticiple(kawulDyn, "passive", { gender: "masc", number: "singular"})) - .toEqual({ - type: "welded", - left: { - type: "VB", - ps: [{ p: "کړل", f: "kRul" }], - }, - right: { - type: "VB", - ps: [{ p: "شوی", f: "shúwey" }], - gender: "masc", - number: "singular", - }, - }); - expect(getPastParticiple(bandawul, "passive", { gender: "fem", number: "plural" })) - .toEqual({ - type: "welded", - left: { - type: "welded", - left: { - type: "NComp", - comp: { - type: "AdjComp", - ps: { p: "بندې", f: "bande" }, - gender: "fem", - number: "plural", - }, - }, - right: { - type: "VB", - ps: [{ p: "کړل", f: "kRul" }], - }, - }, - right: { - type: "VB", - ps: [{ "p": "شوې", "f": "shúwe" }], - gender: "fem", - number: "plural" - }, - }); - }) + }, + right: { + type: "VB", + ps: [{ p: "شوې", f: "shúwe" }], + gender: "fem", + number: "plural", + }, + }); + }); }); describe("ability roots and stems", () => { const tests: { - title: string, + title: string; tests: { - verb: T.VerbEntry, - aspect: T.Aspect, - rs: "root" | "stem", - genderNumber?: T.GenderNumber, - result: T.RootsStemsOutput, - voice: T.Voice, - }[], + verb: T.VerbEntry; + aspect: T.Aspect; + rs: "root" | "stem"; + genderNumber?: T.GenderNumber; + result: T.RootsStemsOutput; + voice: T.Voice; + }[]; }[] = [ { - title: "is the aspect's root with the ability endings and then the perfective kedul modal verb stem or root", + title: + "is the aspect's root with the ability endings and then the perfective kedul modal verb stem or root", tests: [ { verb: khatul, @@ -1327,8 +1925,8 @@ describe("ability roots and stems", () => { ], }, }, - { - type: "VB", + { + type: "VB", ps: [{ p: "ش", f: "sh" }], }, ], @@ -1355,8 +1953,8 @@ describe("ability roots and stems", () => { ], }, }, - { - type: "VB", + { + type: "VB", ps: [{ p: "ش", f: "sh" }], }, ], @@ -1388,8 +1986,8 @@ describe("ability roots and stems", () => { ], }, }, - { - type: "VB", + { + type: "VB", ps: [{ p: "ش", f: "sh" }], }, ], @@ -1398,7 +1996,8 @@ describe("ability roots and stems", () => { ], }, { - title: "passive takes the long imperfective passive root (loses aspect), and adds the ability endings", + title: + "passive takes the long imperfective passive root (loses aspect), and adds the ability endings", tests: [ { verb: achawul, @@ -1420,16 +2019,16 @@ describe("ability roots and stems", () => { long: [ { p: "کېدلی", f: "kedúley" }, { p: "کېدلای", f: "kedúlaay" }, - ], + ], short: [ { p: "کېدی", f: "kedéy" }, { p: "کېدای", f: "kedáay" }, - ], + ], }, }, }, - { - type: "VB", + { + type: "VB", ps: [{ p: "ش", f: "sh" }], }, ], @@ -1441,32 +2040,39 @@ describe("ability roots and stems", () => { tests.forEach((x) => { test(x.title, () => { x.tests.forEach((y) => { - expect(getRootStem({ - verb: y.verb, - aspect: y.aspect, - rs: y.rs, - genderNumber: y.genderNumber || { gender: "masc", number: "plural" }, - type: "ability", - voice: y.voice, - })).toEqual(y.result); - }) - }) + expect( + getRootStem({ + verb: y.verb, + aspect: y.aspect, + rs: y.rs, + genderNumber: y.genderNumber || { + gender: "masc", + number: "plural", + }, + type: "ability", + voice: y.voice, + }) + ).toEqual(y.result); + }); + }); }); }); describe("passive roots and stems", () => { test("root plus kedul", () => { - expect(getRootStem({ - verb: wahul, - aspect: "imperfective", - type: "basic", - rs: "stem", - genderNumber: { - gender: "masc", - number: "singular", - }, - voice: "passive", - })).toEqual([ + expect( + getRootStem({ + verb: wahul, + aspect: "imperfective", + type: "basic", + rs: "stem", + genderNumber: { + gender: "masc", + number: "singular", + }, + voice: "passive", + }) + ).toEqual([ [], [ { @@ -1482,17 +2088,19 @@ describe("passive roots and stems", () => { }, ], ]); - expect(getRootStem({ - verb: wahul, - aspect: "imperfective", - type: "basic", - rs: "root", - genderNumber: { - gender: "masc", - number: "singular", - }, - voice: "passive", - })).toEqual([ + expect( + getRootStem({ + verb: wahul, + aspect: "imperfective", + type: "basic", + rs: "root", + genderNumber: { + gender: "masc", + number: "singular", + }, + voice: "passive", + }) + ).toEqual([ [], [ { @@ -1511,17 +2119,19 @@ describe("passive roots and stems", () => { }, ], ]); - expect(getRootStem({ - verb: wahul, - aspect: "perfective", - type: "basic", - rs: "stem", - genderNumber: { - gender: "masc", - number: "singular", - }, - voice: "passive", - })).toEqual([ + expect( + getRootStem({ + verb: wahul, + aspect: "perfective", + type: "basic", + rs: "stem", + genderNumber: { + gender: "masc", + number: "singular", + }, + voice: "passive", + }) + ).toEqual([ [ooPH], [ { @@ -1537,17 +2147,19 @@ describe("passive roots and stems", () => { }, ], ]); - expect(getRootStem({ - verb: wahul, - aspect: "perfective", - type: "basic", - rs: "root", - genderNumber: { - gender: "masc", - number: "singular", - }, - voice: "passive", - })).toEqual([ + expect( + getRootStem({ + verb: wahul, + aspect: "perfective", + type: "basic", + rs: "root", + genderNumber: { + gender: "masc", + number: "singular", + }, + voice: "passive", + }) + ).toEqual([ [ooPH], [ { @@ -1566,17 +2178,19 @@ describe("passive roots and stems", () => { }, ], ]); - expect(getRootStem({ - verb: bandawul, - aspect: "perfective", - type: "basic", - rs: "root", - genderNumber: { - gender: "fem", - number: "singular", - }, - voice: "passive", - })).toEqual([ + expect( + getRootStem({ + verb: bandawul, + aspect: "perfective", + type: "basic", + rs: "root", + genderNumber: { + gender: "fem", + number: "singular", + }, + voice: "passive", + }) + ).toEqual([ [ { type: "NComp", @@ -1605,17 +2219,19 @@ describe("passive roots and stems", () => { }, ], ]); - expect(getRootStem({ - verb: bandawul, - aspect: "imperfective", - type: "basic", - rs: "root", - genderNumber: { - gender: "fem", - number: "singular", - }, - voice: "passive", - })).toEqual([ + expect( + getRootStem({ + verb: bandawul, + aspect: "imperfective", + type: "basic", + rs: "root", + genderNumber: { + gender: "fem", + number: "singular", + }, + voice: "passive", + }) + ).toEqual([ [], [ { @@ -1634,56 +2250,58 @@ describe("passive roots and stems", () => { }, ], ]); - expect(getRootStem({ - verb: stureyKawul, - aspect: "imperfective", - type: "basic", - voice: "passive", - genderNumber: { - gender: "masc", - number: "singular", - }, - rs: "stem", - })).toEqual([ + expect( + getRootStem({ + verb: stureyKawul, + aspect: "imperfective", + type: "basic", + voice: "passive", + genderNumber: { + gender: "masc", + number: "singular", + }, + rs: "stem", + }) + ).toEqual([ [], [ { - "type": "welded", - "left": { - "type": "welded", - "left": { - "type": "NComp", - "comp": { - "type": "AdjComp", - "ps": { - "p": "ستړی", - "f": "stuRey" + type: "welded", + left: { + type: "welded", + left: { + type: "NComp", + comp: { + type: "AdjComp", + ps: { + p: "ستړی", + f: "stuRey", }, - "gender": "masc", - "number": "singular" - } + gender: "masc", + number: "singular", + }, }, - "right": { - "type": "VB", - "ps": [ + right: { + type: "VB", + ps: [ { - "p": "کول", - "f": "kawul" - } - ] - } + p: "کول", + f: "kawul", + }, + ], + }, }, - "right": { - "type": "VB", - "ps": [ + right: { + type: "VB", + ps: [ { - "p": "کېږ", - "f": "kéG" - } - ] + p: "کېږ", + f: "kéG", + }, + ], }, - } - ] + }, + ], ]); }); }); diff --git a/src/lib/src/new-verb-engine/roots-and-stems.ts b/src/lib/src/new-verb-engine/roots-and-stems.ts index c9e0f20..c8a4f5a 100644 --- a/src/lib/src/new-verb-engine/roots-and-stems.ts +++ b/src/lib/src/new-verb-engine/roots-and-stems.ts @@ -6,360 +6,471 @@ * */ -import { - concatPsString, trimOffPs, -} from "../p-text-helpers"; +import { concatPsString, trimOffPs } from "../p-text-helpers"; import * as T from "../../../types"; import { makePsString, removeFVarientsFromVerb } from "../accent-and-ps-utils"; -import { accentOnNFromEnd, countSyllables, removeAccents } from "../accent-helpers"; +import { + accentOnNFromEnd, + countSyllables, + removeAccents, +} from "../accent-helpers"; import { isKawulVerb, isTlulVerb } from "../type-predicates"; -import { vEntry, addAbilityEnding, weld, removeL, addTrailingAccent, tlulPerfectiveStem, getLongVB, possiblePPartLengths, isStatComp, statCompImperfectiveSpace, makeComplement, vTransitivity, isKedul } from "./rs-helpers"; +import { + vEntry, + addAbilityEnding, + weld, + removeL, + addTrailingAccent, + tlulPerfectiveStem, + getLongVB, + possiblePPartLengths, + isStatComp, + statCompImperfectiveSpace, + makeComplement, + vTransitivity, + isKedul, +} from "./rs-helpers"; import { inflectPattern3 } from "./new-inflectors"; import { fmapSingleOrLengthOpts } from "../fmaps"; const statVerb = { - intransitive: vEntry({"ts":1581086654898,"i":11100,"p":"کېدل","f":"kedul","g":"kedul","e":"to become _____","r":2,"c":"v. intrans.","ssp":"ش","ssf":"sh","prp":"شول","prf":"shwul","pprtp":"شوی","pprtf":"shúwey","noOo":true,"ec":"become"}), - transitive: vEntry({"ts":1579015359582,"i":11030,"p":"کول","f":"kawul","g":"kawul","e":"to make ____ ____ (as in \"He's making me angry.\")","r":4,"c":"v. trans.","ssp":"کړ","ssf":"kR","prp":"کړل","prf":"kRul","pprtp":"کړی","pprtf":"kúRey","noOo":true,"ec":"make,makes,making,made,made"}), + intransitive: vEntry({ + ts: 1581086654898, + i: 11100, + p: "کېدل", + f: "kedul", + g: "kedul", + e: "to become _____", + r: 2, + c: "v. intrans.", + ssp: "ش", + ssf: "sh", + prp: "شول", + prf: "shwul", + pprtp: "شوی", + pprtf: "shúwey", + noOo: true, + ec: "become", + }), + transitive: vEntry({ + ts: 1579015359582, + i: 11030, + p: "کول", + f: "kawul", + g: "kawul", + e: 'to make ____ ____ (as in "He\'s making me angry.")', + r: 4, + c: "v. trans.", + ssp: "کړ", + ssf: "kR", + prp: "کړل", + prf: "kRul", + pprtp: "کړی", + pprtf: "kúRey", + noOo: true, + ec: "make,makes,making,made,made", + }), }; const shwulVB: T.VBBasic = { - type: "VB", - ps: { - long: [{ p: "شول", f: "shwul" }], - short: [{ p: "شو", f: "shw" }], - }, -} + type: "VB", + ps: { + long: [{ p: "شول", f: "shwul" }], + short: [{ p: "شو", f: "shw" }], + }, +}; const shVB: T.VBBasic = { - type: "VB", - ps: [{ p: "ش", f: "sh" }], -} + type: "VB", + ps: [{ p: "ش", f: "sh" }], +}; // TODO: figure out how to handle dynamic / stative verbs -export function getRootStem({ verb, rs, aspect, type, genderNumber, voice }: { - verb: T.VerbEntry, - rs: "root" | "stem", - aspect: T.Aspect, - voice: T.Voice, - type: "basic" | "ability", - genderNumber: { - gender: T.Gender, - number: T.NounNumber, - }, +export function getRootStem({ + verb, + rs, + aspect, + type, + genderNumber, + voice, +}: { + verb: T.VerbEntry; + rs: "root" | "stem"; + aspect: T.Aspect; + voice: T.Voice; + type: "basic" | "ability"; + genderNumber: { + gender: T.Gender; + number: T.NounNumber; + }; }): T.RootsStemsOutput { - const v = removeFVarientsFromVerb(verb); - if (type === "ability") { - return getAbilityRs(v, aspect, rs, voice, genderNumber); - } - if (voice === "passive") { - return getPassiveRs(v, aspect, rs, genderNumber); - } - return rs === "stem" - ? getStem(v, genderNumber, aspect) - : getRoot(v, genderNumber, aspect); + const v = removeFVarientsFromVerb(verb); + if (type === "ability") { + return getAbilityRs(v, aspect, rs, voice, genderNumber); + } + if (voice === "passive") { + return getPassiveRs(v, aspect, rs, genderNumber); + } + return rs === "stem" + ? getStem(v, genderNumber, aspect) + : getRoot(v, genderNumber, aspect); } function getAbilityRs( - verb: T.VerbEntryNoFVars, - aspect: T.Aspect, - rs: "root" | "stem", - voice: T.Voice, - genderNum: T.GenderNumber, + verb: T.VerbEntryNoFVars, + aspect: T.Aspect, + rs: "root" | "stem", + voice: T.Voice, + genderNum: T.GenderNumber ): [[] | [T.VHead], [T.VB, T.VBA]] { - const losesAspect = isTlulVerb(verb) || (isStatComp(verb) && vTransitivity(verb) === "intransitive"); - const [vhead, [basicroot]] = voice === "passive" - ? getPassiveRs(verb, "imperfective", "root", genderNum) - : getRoot(verb, genderNum, losesAspect ? "imperfective" : aspect); - return [ - vhead, - [ - addAbilityEnding(basicroot), - rs === "root" ? shwulVB : shVB, - ], - ]; + const losesAspect = + isTlulVerb(verb) || + (isStatComp(verb) && vTransitivity(verb) === "intransitive"); + const [vhead, [basicroot]] = + voice === "passive" + ? getPassiveRs(verb, "imperfective", "root", genderNum) + : getRoot(verb, genderNum, losesAspect ? "imperfective" : aspect); + return [vhead, [addAbilityEnding(basicroot), rs === "root" ? shwulVB : shVB]]; } -export function getPastParticiple(verb: T.VerbEntry, voice: T.Voice, { gender, number }: { gender: T.Gender, number: T.NounNumber }): T.VBGenNum | T.WeldedGN { - const v = removeFVarientsFromVerb(verb); - if (voice === "passive") { - return getPassivePp(v, { gender, number }); - } - if (isStatComp(v) && v.complement) { - return weld( - makeComplement(v.complement, { gender, number }), - getPastParticiple( - statVerb[vTransitivity(verb)], - voice, - { gender, number }, - ) as T.VBGenNum, - ); - } - if (verb.entry.pprtp && verb.entry.pprtf) { - const base = makePsString(verb.entry.pprtp, verb.entry.pprtf); - return { - type: "VB", - ps: inflectPattern3(base, { gender, number }), - gender, - number, - }; - } - const basicRoot = getRoot(removeFVarientsFromVerb(verb), { gender, number }, "imperfective")[1][0]; - const longRoot = getLongVB(basicRoot); - const rootWLengths = possiblePPartLengths(longRoot); - /* istanbul ignore next */ - if ("right" in rootWLengths) { - throw new Error("should not have welded here"); - } - return { - ...rootWLengths, - ps: addTail(rootWLengths.ps), +export function getPastParticiple( + verb: T.VerbEntry, + voice: T.Voice, + { gender, number }: { gender: T.Gender; number: T.NounNumber } +): T.VBGenNum | T.WeldedGN { + const v = removeFVarientsFromVerb(verb); + if (voice === "passive") { + return getPassivePp(v, { gender, number }); + } + if (isStatComp(v) && v.complement) { + return weld( + makeComplement(v.complement, { gender, number }), + getPastParticiple(statVerb[vTransitivity(verb)], voice, { gender, number, + }) as T.VBGenNum + ); + } + if (verb.entry.pprtp && verb.entry.pprtf) { + const base = makePsString(verb.entry.pprtp, verb.entry.pprtf); + return { + type: "VB", + ps: inflectPattern3(base, { gender, number }), + gender, + number, }; - - function addTail(ps: T.SingleOrLengthOpts): T.SingleOrLengthOpts { - return fmapSingleOrLengthOpts((x) => { - const withTail = concatPsString(x[0], { p: "ی", f: "ey"}); - return inflectPattern3(withTail, { gender, number }); - }, ps); - } + } + const basicRoot = getRoot( + removeFVarientsFromVerb(verb), + { gender, number }, + "imperfective" + )[1][0]; + const longRoot = getLongVB(basicRoot); + const rootWLengths = possiblePPartLengths(longRoot); + /* istanbul ignore next */ + if ("right" in rootWLengths) { + throw new Error("should not have welded here"); + } + return { + ...rootWLengths, + ps: addTail(rootWLengths.ps), + gender, + number, + }; + + function addTail( + ps: T.SingleOrLengthOpts + ): T.SingleOrLengthOpts { + return fmapSingleOrLengthOpts((x) => { + const withTail = concatPsString(x[0], { p: "ی", f: "ey" }); + return inflectPattern3(withTail, { gender, number }); + }, ps); + } } -function getPassivePp(verb: T.VerbEntryNoFVars, genderNumber: T.GenderNumber): T.WeldedGN { - if (isStatComp(verb) && verb.complement) { - return weld( - makeComplement(verb.complement, genderNumber), - getPassivePp(statVerb.transitive, genderNumber), +function getPassivePp( + verb: T.VerbEntryNoFVars, + genderNumber: T.GenderNumber +): T.WeldedGN { + if (isStatComp(verb) && verb.complement) { + return weld( + makeComplement(verb.complement, genderNumber), + getPassivePp(statVerb.transitive, genderNumber) + ); + } + const basicRoot = getRoot( + verb, + genderNumber, + isKawulVerb(verb) ? "perfective" : "imperfective" + )[1][0]; + const longRoot = getLongVB(basicRoot); + const kedulVb: T.VBGenNum = getPastParticiple( + statVerb.intransitive, + "active", + genderNumber + ) as T.VBGenNum; + return weld(longRoot, kedulVb); +} + +function getPassiveRs( + verb: T.VerbEntryNoFVars, + aspect: T.Aspect, + rs: "root" | "stem", + genderNumber: T.GenderNumber +): [[] | [T.VHead], [T.VBA]] { + const [vHead, [basicRoot]] = getRoot(verb, genderNumber, aspect); + const longRoot = getLongVB(basicRoot); + const kedulVba = getRootStem({ + verb: statVerb.intransitive, + aspect, + rs, + type: "basic", + voice: "active", + genderNumber: { gender: "masc", number: "singular" }, + })[1][0] as T.VBBasic; + return [vHead, [weld(longRoot, kedulVba)]]; +} + +function getRoot( + verb: T.VerbEntryNoFVars, + genderNum: T.GenderNumber, + aspect: T.Aspect +): [[T.VHead] | [], [T.VBA]] { + if ( + verb.complement && + isStatComp(verb) && + (aspect === "perfective" || statCompImperfectiveSpace(verb)) + ) { + const auxStem = getRoot( + statVerb[vTransitivity(verb)], + genderNum, + aspect + )[1][0] as T.VBBasic; + const complement = makeComplement(verb.complement, genderNum); + return aspect === "perfective" + ? [[complement], [auxStem]] + : [[], [weld(complement, auxStem)]]; + } + const base = + aspect === "imperfective" + ? accentOnNFromEnd(makePsString(verb.entry.p, verb.entry.f), 0) + : removeAccents( + verb.entry.prp && verb.entry.prf + ? makePsString(verb.entry.prp, verb.entry.prf) + : makePsString(verb.entry.p, verb.entry.f) ); - } - const basicRoot = getRoot(verb, genderNumber, isKawulVerb(verb) ? "perfective" : "imperfective")[1][0]; - const longRoot = getLongVB(basicRoot); - const kedulVb: T.VBGenNum = getPastParticiple(statVerb.intransitive, "active", genderNumber) as T.VBGenNum; - return weld(longRoot, kedulVb); -} - -function getPassiveRs(verb: T.VerbEntryNoFVars, aspect: T.Aspect, rs: "root" | "stem", genderNumber: T.GenderNumber): [[] | [T.VHead], [T.VBA]] { - const [vHead, [basicRoot]] = getRoot(verb, genderNumber, aspect); - const longRoot = getLongVB(basicRoot); - const kedulVba = getRootStem({ verb: statVerb.intransitive, aspect, rs, type: "basic", voice: "active", genderNumber: { gender: "masc", number: "singular" }})[1][0] as T.VBBasic; + const [perfectiveHead, rest] = + aspect === "perfective" ? getPerfectiveHead(base, verb) : [undefined, base]; + if (verb.entry.f === "tlul" && aspect === "perfective") { return [ - vHead, - [weld(longRoot, kedulVba)], + [{ type: "PH", ps: { p: "لا", f: "láa" } }], + [ + { + type: "VB", + ps: { + long: [{ p: "ړل", f: "Rul" }], + short: [{ p: "ړ", f: "R" }], + }, + }, + ], ]; -} - -function getRoot(verb: T.VerbEntryNoFVars, genderNum: T.GenderNumber, aspect: T.Aspect): [[T.VHead] | [], [T.VBA]] { - if (verb.complement && isStatComp(verb) && (aspect === "perfective" || statCompImperfectiveSpace(verb))) { - const auxStem = getRoot( - statVerb[vTransitivity(verb)], - genderNum, - aspect, - )[1][0] as T.VBBasic; - const complement = makeComplement(verb.complement, genderNum); - return aspect === "perfective" - ? [[complement], [auxStem]] - : [[], [weld(complement, auxStem)]]; - } - const base = aspect === "imperfective" - ? accentOnNFromEnd(makePsString(verb.entry.p, verb.entry.f), 0) - : removeAccents( - (verb.entry.prp && verb.entry.prf) - ? makePsString(verb.entry.prp, verb.entry.prf) - : makePsString(verb.entry.p, verb.entry.f) - ); - const [perfectiveHead, rest] = aspect === "perfective" - ? getPerfectiveHead(base, verb) - : [undefined, base]; - return [ - perfectiveHead ? [perfectiveHead] : [], - [ - { - type: "VB", - ps: aspect === "imperfective" - ? { - long: [rest], - short: [addTrailingAccent(removeL(rest))], + } + return [ + perfectiveHead ? [perfectiveHead] : [], + [ + { + type: "VB", + ps: + aspect === "imperfective" + ? { + long: [rest], + short: [addTrailingAccent(removeL(rest))], + } + : { + long: [rest], + short: [removeL(rest)], + ...(aspect === "perfective" && isKawulVerb(verb) + ? { + mini: [{ p: "ک", f: "k" }], } - : { - long: [rest], - short: [removeL(rest)], - ...(aspect === "perfective" && isKawulVerb(verb)) ? { - mini: [{ p: "ک", f: "k" }], - } : {}, - }, - }, - ], - ]; + : {}), + }, + }, + ], + ]; } -function getStem(verb: T.VerbEntryNoFVars, genderNum: T.GenderNumber, aspect: T.Aspect): [[T.VHead] | [], [T.VB]] { - const statComp = isStatComp(verb); - if (verb.complement && statComp && (aspect === "perfective" || statCompImperfectiveSpace(verb))) { - const auxStem = getStem( - statVerb[vTransitivity(verb)], - genderNum, - aspect, - )[1][0] as T.VBBasic; - const complement = makeComplement(verb.complement, genderNum); - return aspect === "perfective" - ? [[complement], [auxStem]] - : [[], [weld(complement, auxStem)]]; +function getStem( + verb: T.VerbEntryNoFVars, + genderNum: T.GenderNumber, + aspect: T.Aspect +): [[T.VHead] | [], [T.VB]] { + const statComp = isStatComp(verb); + if ( + verb.complement && + statComp && + (aspect === "perfective" || statCompImperfectiveSpace(verb)) + ) { + const auxStem = getStem( + statVerb[vTransitivity(verb)], + genderNum, + aspect + )[1][0] as T.VBBasic; + const complement = makeComplement(verb.complement, genderNum); + return aspect === "perfective" + ? [[complement], [auxStem]] + : [[], [weld(complement, auxStem)]]; + } + if (aspect === "perfective") { + if (verb.entry.f === "tlul") { + return tlulPerfectiveStem(genderNum); } - if (aspect === "perfective") { - if (verb.entry.f === "tlul") { - return tlulPerfectiveStem(genderNum); - } - if (!isKedul(verb) && vTransitivity(verb) === "intransitive" && verb.entry.p.endsWith("ېدل")) { - return splitEdulIntans(edulIntransBase(verb)) - } - const base: T.PsString = (verb.entry.ssp && verb.entry.ssf) - // with irregular perfective stem - ? makePsString(verb.entry.ssp, verb.entry.ssf) - : (verb.entry.psp && verb.entry.psf) - // with perfective stem based on irregular perfective root - ? makePsString(verb.entry.psp, verb.entry.psf) - // with regular infinitive based perfective stem - : removeL(makePsString(verb.entry.p, verb.entry.f)); - const [perfectiveHead, rest] = getPerfectiveHead(base, verb); - return [ - perfectiveHead ? [perfectiveHead] : [], - [ - { - type: "VB", - ps: isKawulVerb(verb) ? kawulSpecialPerfective : [rest], - }, - ], - ]; + if ( + !isKedul(verb) && + vTransitivity(verb) === "intransitive" && + verb.entry.p.endsWith("ېدل") + ) { + return splitEdulIntans(edulIntransBase(verb)); } - const rawBase = removeL(makePsString(verb.entry.p, verb.entry.f)); - const base = verb.entry.psp && verb.entry.psf - ? [makePsString(verb.entry.psp, verb.entry.psf)] - : (vTransitivity(verb) === "intransitive" && rawBase.p.endsWith("ېد")) - ? edulIntransBase(verb) - : isKawulVerb(verb) || statComp || (countSyllables(rawBase) > 1 && rawBase.f.endsWith("aw")) - ? [addTrailingAccent(rawBase)] - : [rawBase]; + const base: T.PsString = + verb.entry.ssp && verb.entry.ssf + ? // with irregular perfective stem + makePsString(verb.entry.ssp, verb.entry.ssf) + : verb.entry.psp && verb.entry.psf + ? // with perfective stem based on irregular perfective root + makePsString(verb.entry.psp, verb.entry.psf) + : // with regular infinitive based perfective stem + removeL(makePsString(verb.entry.p, verb.entry.f)); + const [perfectiveHead, rest] = getPerfectiveHead(base, verb); return [ - [], - [ - { - type: "VB", - ps: base, - }, - ], + perfectiveHead ? [perfectiveHead] : [], + [ + { + type: "VB", + ps: isKawulVerb(verb) ? kawulSpecialPerfective : [rest], + }, + ], ]; - function splitEdulIntans(ps: T.SingleOrLengthOpts): [[T.PH] | [], [T.VB]] { - const [ph, long] = ("long" in ps) - ? getPerfectiveHead(ps.long[0], verb) - : getPerfectiveHead(ps[0], verb) - const short = ("long" in ps) - ? getPerfectiveHead(ps.short[0], verb) - : undefined; - if (short) { - return [ - ph ? [ph] : [], - [ - { - type: "VB", - ps: { - long: [long], - short: [short[1]], - }, - }, - ], - ]; - } - return [ - ph ? [ph] : [], - [ - { type: "VB", ps: [long] }, - ], - ]; + } + const rawBase = removeL(makePsString(verb.entry.p, verb.entry.f)); + const base = + verb.entry.psp && verb.entry.psf + ? [makePsString(verb.entry.psp, verb.entry.psf)] + : vTransitivity(verb) === "intransitive" && rawBase.p.endsWith("ېد") + ? edulIntransBase(verb) + : isKawulVerb(verb) || + statComp || + (countSyllables(rawBase) > 1 && rawBase.f.endsWith("aw")) + ? [addTrailingAccent(rawBase)] + : [rawBase]; + return [ + [], + [ + { + type: "VB", + ps: base, + }, + ], + ]; + function splitEdulIntans( + ps: T.SingleOrLengthOpts + ): [[T.PH] | [], [T.VB]] { + const [ph, long] = + "long" in ps + ? getPerfectiveHead(ps.long[0], verb) + : getPerfectiveHead(ps[0], verb); + const short = + "long" in ps ? getPerfectiveHead(ps.short[0], verb) : undefined; + if (short) { + return [ + ph ? [ph] : [], + [ + { + type: "VB", + ps: { + long: [long], + short: [short[1]], + }, + }, + ], + ]; } + return [ph ? [ph] : [], [{ type: "VB", ps: [long] }]]; + } } // TODO: This is a nasty and messy way to do it with the length options included -function getPerfectiveHead(base: T.PsString, v: T.VerbEntryNoFVars): [T.PH, T.PsString] | [undefined, T.PsString] { - // if ((verb.entry.ssp && verb.entry.ssf) || verb.entry.separationAtP) { - // // handle split - // } - if (v.entry.separationAtP && v.entry.separationAtF) { - const ph: T.PH = { - type: "PH", - ps: accentOnNFromEnd({ - p: base.p.slice(0, v.entry.separationAtP), - f: base.f.slice(0, v.entry.separationAtF), - }, 0), - }; - const rest = { - p: base.p.slice(v.entry.separationAtP), - f: base.f.slice(v.entry.separationAtF), - }; - return [ph, rest]; - } - const [ph, rest]: [T.PH | undefined, T.PsString] = v.entry.noOo - ? [undefined, base] - : v.entry.sepOo - ? [ - { type: "PH", ps: { p: "و ", f: "óo`"}}, - base, - ] - : ["آ", "ا"].includes(base.p.charAt(0)) && base.f.charAt(0) === "a" - ? [ - { type: "PH", ps: { p: "وا", f: "wáa" }}, - removeAStart(base), - ] - : ["óo", "oo"].includes(base.f.slice(0, 2)) - ? [ - { type: "PH", ps: { p: "و", f: "wÚ" }}, - base, - ] - : ["ée", "ee"].includes(base.f.slice(0, 2)) && base.p.slice(0, 2) === "ای" - ? [ - { type: "PH", ps: { p: "وي", f: "wée" }}, - { - p: base.p.slice(2), - f: base.f.slice(2), - }, - ] : ["é", "e"].includes(base.f.slice(0, 2)) && base.p.slice(0, 2) === "اې" - ? [ - { type: "PH", ps: { p: "وي", f: "wé" }}, - { - p: base.p.slice(2), - f: base.f.slice(1), - }, - ] : ["ó", "o"].includes(base.f[0]) && base.p.slice(0, 2) === "او" - ? [ - { type: "PH", ps: { p: "و", f: "óo`"}}, - base, - ] : [ - { type: "PH", ps: { p: "و", f: "óo" }}, - base, - ]; - return [ - ph, - removeAccents(rest), - ]; - function removeAStart(ps: T.PsString) { - return { - p: ps.p.slice(1), - f: ps.f.slice(ps.f[1] === "a" ? 2 : 1), - }; - } +function getPerfectiveHead( + base: T.PsString, + v: T.VerbEntryNoFVars +): [T.PH, T.PsString] | [undefined, T.PsString] { + // if ((verb.entry.ssp && verb.entry.ssf) || verb.entry.separationAtP) { + // // handle split + // } + if (v.entry.separationAtP && v.entry.separationAtF) { + const ph: T.PH = { + type: "PH", + ps: accentOnNFromEnd( + { + p: base.p.slice(0, v.entry.separationAtP), + f: base.f.slice(0, v.entry.separationAtF), + }, + 0 + ), + }; + const rest = { + p: base.p.slice(v.entry.separationAtP), + f: base.f.slice(v.entry.separationAtF), + }; + return [ph, rest]; + } + const [ph, rest]: [T.PH | undefined, T.PsString] = v.entry.noOo + ? [undefined, base] + : v.entry.sepOo + ? [{ type: "PH", ps: { p: "و ", f: "óo`" } }, base] + : ["آ", "ا"].includes(base.p.charAt(0)) && base.f.charAt(0) === "a" + ? [{ type: "PH", ps: { p: "وا", f: "wáa" } }, removeAStart(base)] + : ["óo", "oo"].includes(base.f.slice(0, 2)) + ? [{ type: "PH", ps: { p: "و", f: "wÚ" } }, base] + : ["ée", "ee"].includes(base.f.slice(0, 2)) && base.p.slice(0, 2) === "ای" + ? [ + { type: "PH", ps: { p: "وي", f: "wée" } }, + { + p: base.p.slice(2), + f: base.f.slice(2), + }, + ] + : ["é", "e"].includes(base.f.slice(0, 2)) && base.p.slice(0, 2) === "اې" + ? [ + { type: "PH", ps: { p: "وي", f: "wé" } }, + { + p: base.p.slice(2), + f: base.f.slice(1), + }, + ] + : ["ó", "o"].includes(base.f[0]) && base.p.slice(0, 2) === "او" + ? [{ type: "PH", ps: { p: "و", f: "óo`" } }, base] + : [{ type: "PH", ps: { p: "و", f: "óo" } }, base]; + return [ph, removeAccents(rest)]; + function removeAStart(ps: T.PsString) { + return { + p: ps.p.slice(1), + f: ps.f.slice(ps.f[1] === "a" ? 2 : 1), + }; + } } -function edulIntransBase(v: T.VerbEntryNoFVars): T.SingleOrLengthOpts { - const base = trimOffPs(makePsString(v.entry.p, v.entry.f), 3, 4); - const long: T.PsString[] = [concatPsString( - base, - { p: "ېږ", f: "éG" }, - )]; - const short: T.PsString[] | undefined = (v.entry.shortIntrans) - ? [base] - : undefined; - return short ? { short, long } : long; +function edulIntransBase( + v: T.VerbEntryNoFVars +): T.SingleOrLengthOpts { + const base = trimOffPs(makePsString(v.entry.p, v.entry.f), 3, 4); + const long: T.PsString[] = [concatPsString(base, { p: "ېږ", f: "éG" })]; + const short: T.PsString[] | undefined = v.entry.shortIntrans + ? [base] + : undefined; + return short ? { short, long } : long; } const kawulSpecialPerfective: T.LengthOptions = { - long: [{ p: "کړ", f: "kR" }], - short: [{ p: "ک", f: "k" }], -}; \ No newline at end of file + long: [{ p: "کړ", f: "kR" }], + short: [{ p: "ک", f: "k" }], +}; diff --git a/src/lib/src/phrase-building/compile.ts b/src/lib/src/phrase-building/compile.ts index baef606..f6adca1 100644 --- a/src/lib/src/phrase-building/compile.ts +++ b/src/lib/src/phrase-building/compile.ts @@ -1,39 +1,35 @@ import * as T from "../../../types"; import { - capitalizeFirstLetter, - concatPsString, getLong, + capitalizeFirstLetter, + concatPsString, + getLong, } from "../p-text-helpers"; import { negativeParticle } from "../grammar-units"; import * as grammarUnits from "../grammar-units"; -import { - removeDuplicates, -} from "./vp-tools"; +import { removeDuplicates } from "./vp-tools"; import { getEnglishFromRendered, getPashtoFromRendered } from "./np-tools"; import { completeEPSelection, renderEP } from "./render-ep"; import { completeVPSelection } from "./vp-tools"; import { renderVP } from "./render-vp"; import { - getAPsFromBlocks, - getComplementFromBlocks, - getObjectSelectionFromBlocks, - getPredicateSelectionFromBlocks, - getSubjectSelectionFromBlocks, - hasEquativeWithLengths, - isRenderedVerbB, - specifyEquativeLength, + getAPsFromBlocks, + getComplementFromBlocks, + getObjectSelectionFromBlocks, + getPredicateSelectionFromBlocks, + getSubjectSelectionFromBlocks, + hasEquativeWithLengths, + isRenderedVerbB, + specifyEquativeLength, } from "./blocks-utils"; -import { - blank, - kidsBlank, -} from "../misc-helpers"; +import { blank, kidsBlank } from "../misc-helpers"; type BlankoutOptions = { - equative?: boolean, - ba?: boolean, - kidsSection?: boolean, - verb?: boolean, - negative?: boolean, - predicate?: boolean, + equative?: boolean; + ba?: boolean; + kidsSection?: boolean; + verb?: boolean; + negative?: boolean; + predicate?: boolean; }; // function compilePs({ blocks, kids, verb: { head, rest }, VP }: CompilePsInput): T.SingleOrLengthOpts { @@ -61,357 +57,506 @@ type BlankoutOptions = { // })); // } -export function compileEP(EP: T.EPRendered): { ps: T.SingleOrLengthOpts, e?: string[] }; -export function compileEP(EP: T.EPRendered, combineLengths: true, blankOut?: BlankoutOptions): { ps: T.PsString[], e?: string[] }; -export function compileEP(EP: T.EPRendered, combineLengths?: boolean, blankOut?: BlankoutOptions): { ps: T.SingleOrLengthOpts, e?: string[] } { - const psResult = compileEPPs(EP.blocks, EP.kids, EP.omitSubject, blankOut); +export function compileEP(EP: T.EPRendered): { + ps: T.SingleOrLengthOpts; + e?: string[]; +}; +export function compileEP( + EP: T.EPRendered, + combineLengths: true, + blankOut?: BlankoutOptions +): { ps: T.PsString[]; e?: string[] }; +export function compileEP( + EP: T.EPRendered, + combineLengths?: boolean, + blankOut?: BlankoutOptions +): { ps: T.SingleOrLengthOpts; e?: string[] } { + const psResult = compileEPPs(EP.blocks, EP.kids, EP.omitSubject, blankOut); + return { + ps: combineLengths ? flattenLengths(psResult) : psResult, + e: compileEnglishEP(EP), + }; +} + +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 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 + ), + }; +} + +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( + filterForVisibleBlocksVP(blocks, form, king), + kids, + !!blankOut?.ba + ); + return removeDuplicates( + combineIntoText(blocksWKids, subjectPerson, blankOut) + ); +} + +function compileEPPs( + blocks: T.Block[][], + kids: T.Kid[], + omitSubject: boolean, + blankOut?: BlankoutOptions +): T.SingleOrLengthOpts { + if (hasEquativeWithLengths(blocks)) { return { - ps: combineLengths ? flattenLengths(psResult) : psResult, - e: compileEnglishEP(EP), + long: compileEPPs( + specifyEquativeLength(blocks, "long"), + kids, + omitSubject, + blankOut + ) as T.PsString[], + short: compileEPPs( + specifyEquativeLength(blocks, "short"), + kids, + omitSubject, + blankOut + ) as T.PsString[], }; + } + const subjectPerson = + getSubjectSelectionFromBlocks(blocks).selection.selection.person; + const blocksWKids = putKidsInKidsSection( + omitSubject + ? blocks.map((blks) => + blks.filter((b) => b.block.type !== "subjectSelection") + ) + : blocks, + kids, + !!blankOut?.kidsSection + ); + return removeDuplicates( + combineIntoText(blocksWKids, subjectPerson, 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 psResult = compileVPPs(VP.blocks, VP.kids, form, VP.king, blankOut); +export function filterForVisibleBlocksVP( + blocks: T.Block[][], + form: T.FormVersion, + king: "subject" | "object" +): T.Block[][] { + const servant = king === "object" ? "subject" : "object"; + return blocks.map((blks) => + blks.filter((block) => { + if (form.removeKing) { + if ( + (king === "subject" && block.block.type === "subjectSelection") || + (king === "object" && block.block.type === "objectSelection") + ) + return false; + } + if (form.shrinkServant) { + if ( + (servant === "subject" && block.block.type === "subjectSelection") || + (servant === "object" && block.block.type === "objectSelection") + ) + return false; + } + if ( + block.block.type === "objectSelection" && + typeof block.block.selection !== "object" + ) { + return false; + } + return true; + }) + ); +} + +export function filterForVisibleBlocksEP( + blocks: T.Block[][], + omitSubject: boolean +): T.Block[][] { + if (!omitSubject) return blocks; + return blocks.map((blks) => + blks.filter(({ block }) => { + if (block.type === "subjectSelection") { + return false; + } + return true; + }) + ); +} + +export function combineIntoText( + piecesWVars: (T.Block | T.Kid | T.PsString)[][], + subjectPerson: T.Person, + blankOut?: BlankoutOptions +): T.PsString[] { + function removeDoubleBlanks(x: T.PsString): T.PsString { 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), + p: x.p.replace(blank.p + blank.p, blank.p), + f: x.f.replace(blank.f + blank.f, blank.f), }; -} - -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( - filterForVisibleBlocksVP(blocks, form, king), - kids, - !!blankOut?.ba - ); - return removeDuplicates(combineIntoText(blocksWKids, subjectPerson, blankOut)); -} - -function compileEPPs(blocks: T.Block[][], kids: T.Kid[], omitSubject: boolean, blankOut?: BlankoutOptions): T.SingleOrLengthOpts { - if (hasEquativeWithLengths(blocks)) { - return { - long: compileEPPs(specifyEquativeLength(blocks, "long"), kids, omitSubject, blankOut) as T.PsString[], - short: compileEPPs(specifyEquativeLength(blocks, "short"), kids, omitSubject, blankOut) as T.PsString[], - }; + } + function combine(pieces: (T.Block | T.Kid | T.PsString)[]): T.PsString[] { + const first = pieces[0]; + const next = pieces[1]; + const rest = pieces.slice(1); + // better to do map-reduce + // map the blocks into monoids [T.PsString] (with appropriate space blocks) and then concat them all together + const firstPs = + "p" in first + ? [first] + : (blankOut?.equative && + "block" in first && + first.block.type === "equative") || + (blankOut?.verb && "block" in first && isRenderedVerbB(first)) || + (blankOut?.predicate && + "block" in first && + first.block.type === "predicateSelection") + ? [blank] + : blankOut?.ba && "kid" in first && first.kid.type === "ba" + ? [kidsBlank] + : blankOut?.negative && + "block" in first && + first.block.type === "negative" + ? [{ p: "", f: "" }] + : getPsFromPiece(first, subjectPerson); + if (!rest.length) { + return firstPs; } - const subjectPerson = getSubjectSelectionFromBlocks(blocks) - .selection.selection.person; - const blocksWKids = putKidsInKidsSection( - omitSubject ? blocks.map(blks => blks.filter(b => b.block.type !== "subjectSelection")) : blocks, - kids, - !!blankOut?.kidsSection - ); - return removeDuplicates(combineIntoText(blocksWKids, subjectPerson, blankOut)); + return combine(rest) + .flatMap((r) => + firstPs.map((fPs) => + concatPsString( + fPs, + // TODO: this spacing is a mess and not accurate + !("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 + ) + ) + ) + .map(removeDoubleBlanks); + } + return piecesWVars.flatMap(combine); } -export function filterForVisibleBlocksVP(blocks: T.Block[][], form: T.FormVersion, king: "subject" | "object"): T.Block[][] { - const servant = king === "object" ? "subject" : "object"; - return blocks.map(blks => blks.filter((block) => { - if (form.removeKing) { - if ( - (king === "subject" && block.block.type === "subjectSelection") - || - (king === "object" && block.block.type === "objectSelection") - ) return false; - } - if (form.shrinkServant) { - if ( - (servant === "subject" && block.block.type === "subjectSelection") - || - (servant === "object" && block.block.type === "objectSelection") - ) return false; - } - if (block.block.type === "objectSelection" && typeof block.block.selection !== "object") { - return false; - } - return true; - })); -} - -export function filterForVisibleBlocksEP(blocks: T.Block[][], omitSubject: boolean): T.Block[][] { - if (!omitSubject) return blocks; - return blocks.map(blks => blks.filter(({ block }) => { - if (block.type === "subjectSelection") { - return false; - } - return true; - })); -} - -function combineIntoText(piecesWVars: (T.Block | T.Kid | T.PsString)[][], subjectPerson: T.Person, blankOut?: BlankoutOptions): T.PsString[] { - function removeDoubleBlanks(x: T.PsString): T.PsString { - return { - p: x.p.replace(blank.p+blank.p, blank.p), - f: x.f.replace(blank.f+blank.f, blank.f), - }; +function getPsFromPiece( + piece: T.Block | T.Kid, + subjectPerson: T.Person +): T.PsString[] { + if ("block" in piece) { + if (piece.block.type === "negative") { + return [ + negativeParticle[ + piece.block.imperative ? "imperative" : "nonImperative" + ], + ]; } - function combine(pieces: (T.Block | T.Kid | T.PsString)[]): T.PsString[] { - const first = pieces[0]; - const next = pieces[1]; - const rest = pieces.slice(1); - const firstPs = ("p" in first) - ? [first] - : ( - (blankOut?.equative && "block" in first && first.block.type === "equative") || - (blankOut?.verb && "block" in first && isRenderedVerbB(first)) || - (blankOut?.predicate && "block" in first && first.block.type === "predicateSelection") - ) - ? [blank] - : ((blankOut?.ba) && "kid" in first && first.kid.type === "ba") - ? [kidsBlank] - : (blankOut?.negative && "block" in first && first.block.type === "negative") - ? [{ p: "", f: "" }] - : getPsFromPiece(first, subjectPerson); - if (!rest.length) { - return firstPs; - } - return combine(rest).flatMap(r => ( - firstPs.map(fPs => concatPsString( - fPs, - // TODO: this spacing is a mess and not accurate - (!("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, - )) - ) - ).map(removeDoubleBlanks); + if (piece.block.type === "equative") { + // length will already be specified in compileEPPs - this is just for type safety + return getLong(piece.block.equative.ps); } - return piecesWVars.flatMap(combine); -} - -function getPsFromPiece(piece: T.Block | T.Kid, subjectPerson: T.Person): T.PsString[] { - if ("block" in piece) { - if (piece.block.type === "negative") { - return [ - negativeParticle[piece.block.imperative ? "imperative" : "nonImperative"], - ]; - } - if (piece.block.type === "equative") { - // length will already be specified in compileEPPs - this is just for type safety - return getLong(piece.block.equative.ps); - } - if (piece.block.type === "subjectSelection" || piece.block.type === "predicateSelection") { - return getPashtoFromRendered(piece.block.selection, subjectPerson); - } - if (piece.block.type === "AP") { - return getPashtoFromRendered(piece.block, subjectPerson); - } - 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 === "NComp") { - return [piece.block.comp.ps]; - } - if (piece.block.type === "complement") { - if (piece.block.selection.type === "sandwich") { - // TODO: Kinda cheating - return getPashtoFromRendered({ type: "AP", selection: piece.block.selection }, false); - } - return piece.block.selection.ps; - } - // welded - return getPsFromWelded(piece.block); + if ( + piece.block.type === "subjectSelection" || + piece.block.type === "predicateSelection" + ) { + return getPashtoFromRendered(piece.block.selection, subjectPerson); } - if ("kid" in piece) { - if (piece.kid.type === "ba") { - return [grammarUnits.baParticle]; - } - if (piece.kid.type === "mini-pronoun") { - return [piece.kid.ps]; - } + if (piece.block.type === "AP") { + return getPashtoFromRendered(piece.block, subjectPerson); } - throw new Error("unrecognized piece type"); + 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 === "NComp") { + return [piece.block.comp.ps]; + } + if (piece.block.type === "complement") { + if (piece.block.selection.type === "sandwich") { + // TODO: Kinda cheating + return getPashtoFromRendered( + { type: "AP", selection: piece.block.selection }, + false + ); + } + return piece.block.selection.ps; + } + // welded + return getPsFromWelded(piece.block); + } + if ("kid" in piece) { + if (piece.kid.type === "ba") { + return [grammarUnits.baParticle]; + } + if (piece.kid.type === "mini-pronoun") { + return [piece.kid.ps]; + } + } + throw new Error("unrecognized piece type"); } 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); + function getPsFromSide( + v: T.VBBasic | T.Welded | T.NComp | T.VBGenNum + ): T.PsString[] { + if (v.type === "VB") { + return flattenLengths(v.ps); } - const left = getPsFromSide(v.left); - const right = getPsFromSide(v.right); - return left.flatMap(leftVar => ( - right.flatMap(rightVar => ( - concatPsString(leftVar, " ", rightVar) - )) - )); + if (v.type === "NComp") { + return [v.comp.ps]; + } + return getPsFromWelded(v); + } + const left = getPsFromSide(v.left); + const right = getPsFromSide(v.right); + return left.flatMap((leftVar) => + right.flatMap((rightVar) => concatPsString(leftVar, " ", rightVar)) + ); } function getEngAPs(blocks: T.Block[][]): string { - return getAPsFromBlocks(blocks).reduce((accum, curr) => { - const e = getEnglishFromRendered(curr); - if (!e) return accum; - return `${accum} ${e}`; - }, ""); + return getAPsFromBlocks(blocks).reduce((accum, curr) => { + const e = getEnglishFromRendered(curr); + if (!e) return accum; + return `${accum} ${e}`; + }, ""); } 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; + 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)[][] { - function insert(blocks: T.Block[]): (T.Block | T.Kid | T.PsString)[] { - const first = blocks[0]; - const rest = blocks.slice(1); - return [ - first, - ...enforceKidsSectionBlankout ? [kidsBlank] : kids, - ...rest, - ]; - } - return blocksWVars.map(insert); +function putKidsInKidsSection( + blocksWVars: T.Block[][], + kids: T.Kid[], + enforceKidsSectionBlankout: boolean +): (T.Block | T.Kid | T.PsString)[][] { + function insert(blocks: T.Block[]): (T.Block | T.Kid | T.PsString)[] { + const first = blocks[0]; + const rest = blocks.slice(1); + return [ + first, + ...(enforceKidsSectionBlankout ? [kidsBlank] : kids), + ...rest, + ]; + } + return blocksWVars.map(insert); } function compileEnglishVP(VP: T.VPRendered): string[] | undefined { - function insertEWords(e: string, { subject, object, APs, complement }: { subject: string, object?: string, APs: string, complement: string | undefined }): string { - return e - .replace("$SUBJ", subject) - .replace("$OBJ", object || "") - // add the complement in English if it's an external complement from a helper verb (kawul/kedul) - // TODO! - + (complement /* && isStativeHelper(getVerbFromBlocks(VP.blocks).block.verb))*/ - ? ` ${complement}` - : "") - + APs; + function insertEWords( + e: string, + { + subject, + object, + APs, + complement, + }: { + subject: string; + object?: string; + APs: string; + complement: string | undefined; } - const engSubj = getSubjectSelectionFromBlocks(VP.blocks).selection; - const obj = getObjectSelectionFromBlocks(VP.blocks).selection; - const engObj = typeof obj === "object" - ? obj - : (obj === "none" || obj === T.Person.ThirdPlurMale) - ? "" - : undefined; - const engAPs = getEngAPs(VP.blocks); - const engComplement = getEngComplement(VP.blocks); - // require all English parts for making the English phrase - return (VP.englishBase && engSubj && engObj !== undefined) - ? VP.englishBase.map(e => insertEWords(e, { + ): string { + return ( + e.replace("$SUBJ", subject).replace("$OBJ", object || "") + + // add the complement in English if it's an external complement from a helper verb (kawul/kedul) + // TODO! + (complement /* && isStativeHelper(getVerbFromBlocks(VP.blocks).block.verb))*/ + ? ` ${complement}` + : "") + + APs + ); + } + const engSubj = getSubjectSelectionFromBlocks(VP.blocks).selection; + const obj = getObjectSelectionFromBlocks(VP.blocks).selection; + const engObj = + typeof obj === "object" + ? obj + : obj === "none" || obj === T.Person.ThirdPlurMale + ? "" + : undefined; + const engAPs = getEngAPs(VP.blocks); + const engComplement = getEngComplement(VP.blocks); + // require all English parts for making the English phrase + return VP.englishBase && engSubj && engObj !== undefined + ? VP.englishBase + .map((e) => + insertEWords(e, { // TODO: make sure we actually have the english subject: getEnglishFromRendered(engSubj) || "", object: engObj ? getEnglishFromRendered(engObj) : "", APs: engAPs, complement: engComplement, - })).map(capitalizeFirstLetter) - : undefined; + }) + ) + .map(capitalizeFirstLetter) + : undefined; } function compileEnglishEP(EP: T.EPRendered): string[] | undefined { - function insertEWords(e: string, { subject, predicate, APs }: { subject: string, predicate: string, APs: string }): string { - return e.replace("$SUBJ", subject).replace("$PRED", predicate || "") + APs; - } - const engSubjR = getSubjectSelectionFromBlocks(EP.blocks).selection; - const engPredR = getPredicateSelectionFromBlocks(EP.blocks).selection; - const engSubj = getEnglishFromRendered(engSubjR); - const engPred = getEnglishFromRendered(engPredR); - const engAPs = getEngAPs(EP.blocks); - // require all English parts for making the English phrase - const b = (EP.englishBase && engSubj && engPred) - ? EP.englishBase.map(e => insertEWords(e, { + function insertEWords( + e: string, + { + subject, + predicate, + APs, + }: { subject: string; predicate: string; APs: string } + ): string { + return e.replace("$SUBJ", subject).replace("$PRED", predicate || "") + APs; + } + const engSubjR = getSubjectSelectionFromBlocks(EP.blocks).selection; + const engPredR = getPredicateSelectionFromBlocks(EP.blocks).selection; + const engSubj = getEnglishFromRendered(engSubjR); + const engPred = getEnglishFromRendered(engPredR); + const engAPs = getEngAPs(EP.blocks); + // require all English parts for making the English phrase + const b = + EP.englishBase && engSubj && engPred + ? EP.englishBase.map((e) => + insertEWords(e, { subject: engSubj, predicate: engPred, APs: engAPs, - })) - : undefined; - return b?.map(capitalizeFirstLetter); + }) + ) + : undefined; + return b?.map(capitalizeFirstLetter); } -export function checkForMiniPronounsError(s: T.EPSelectionState | T.VPSelectionState): undefined | string { - function findDuplicateMiniP(mp: T.MiniPronoun[]): T.MiniPronoun | undefined { - const duplicates = mp.filter((item, index) => ( - mp.findIndex(m => item.ps.p === m.ps.p) !== index - )); - if (duplicates.length === 0) return undefined; - return duplicates[0]; +export function checkForMiniPronounsError( + s: T.EPSelectionState | T.VPSelectionState +): undefined | string { + function findDuplicateMiniP(mp: T.MiniPronoun[]): T.MiniPronoun | undefined { + const duplicates = mp.filter( + (item, index) => mp.findIndex((m) => item.ps.p === m.ps.p) !== index + ); + if (duplicates.length === 0) return undefined; + return duplicates[0]; + } + const kids = (() => { + if ("predicate" in s) { + const EPS = completeEPSelection(s); + if (!EPS) return undefined; + return renderEP(EPS).kids; } - const kids = (() => { - if ("predicate" in s) { - const EPS = completeEPSelection(s); - if (!EPS) return undefined; - return renderEP(EPS).kids; - }; - const VPS = completeVPSelection(s); - if (!VPS) return undefined; - return renderVP(VPS).kids; - })(); - if (!kids) return undefined; - const miniPronouns = kids.filter(x => x.kid.type === "mini-pronoun").map(m => m.kid) as T.MiniPronoun[]; - if (miniPronouns.length > 2) { - return "can't add another mini-pronoun, there are alread two"; - } - const duplicateMiniPronoun = findDuplicateMiniP(miniPronouns); - if (duplicateMiniPronoun) { - return `there's already a ${duplicateMiniPronoun.ps.p} - ${duplicateMiniPronoun.ps.f} mini-pronoun in use, can't have two of those`; - } - return undefined; + const VPS = completeVPSelection(s); + if (!VPS) return undefined; + return renderVP(VPS).kids; + })(); + if (!kids) return undefined; + const miniPronouns = kids + .filter((x) => x.kid.type === "mini-pronoun") + .map((m) => m.kid) as T.MiniPronoun[]; + if (miniPronouns.length > 2) { + return "can't add another mini-pronoun, there are alread two"; + } + const duplicateMiniPronoun = findDuplicateMiniP(miniPronouns); + if (duplicateMiniPronoun) { + return `there's already a ${duplicateMiniPronoun.ps.p} - ${duplicateMiniPronoun.ps.f} mini-pronoun in use, can't have two of those`; + } + return undefined; } -function findPossesivesInNP(NP: T.Rendered | T.ObjectNP | undefined): T.Rendered[] { - if (NP === undefined) return []; - if (typeof NP !== "object") return []; - if (!NP.selection.possesor) return []; - if (NP.selection.adjectives) { - const { adjectives, ...rest } = NP.selection; - return [ - ...findPossesivesInAdjectives(adjectives), - ...findPossesivesInNP({ type: "NP", selection: rest }), - ]; - } - if (NP.selection.possesor.shrunken) { - return [NP.selection.possesor.np]; - } - return findPossesivesInNP(NP.selection.possesor.np); +function findPossesivesInNP( + NP: T.Rendered | T.ObjectNP | undefined +): T.Rendered[] { + if (NP === undefined) return []; + if (typeof NP !== "object") return []; + if (!NP.selection.possesor) return []; + if (NP.selection.adjectives) { + const { adjectives, ...rest } = NP.selection; + return [ + ...findPossesivesInAdjectives(adjectives), + ...findPossesivesInNP({ type: "NP", selection: rest }), + ]; + } + if (NP.selection.possesor.shrunken) { + return [NP.selection.possesor.np]; + } + return findPossesivesInNP(NP.selection.possesor.np); } -function findPossesivesInAdjectives(a: T.Rendered[]): T.Rendered[] { - return a.reduce((accum, curr): T.Rendered[] => ([ - ...accum, - ...findPossesivesInAdjective(curr), - ]), [] as T.Rendered[]) +function findPossesivesInAdjectives( + a: T.Rendered[] +): T.Rendered[] { + return a.reduce( + (accum, curr): T.Rendered[] => [ + ...accum, + ...findPossesivesInAdjective(curr), + ], + [] as T.Rendered[] + ); } -function findPossesivesInAdjective(a: T.Rendered): T.Rendered[] { - if (!a.sandwich) return []; - return findPossesivesInNP(a.sandwich.inside); +function findPossesivesInAdjective( + a: T.Rendered +): T.Rendered[] { + if (!a.sandwich) return []; + return findPossesivesInNP(a.sandwich.inside); } -export function flattenLengths(r: T.SingleOrLengthOpts): T.PsString[] { - if ("long" in r) { - return Object.values(r).flat(); +export function flattenLengths( + r: T.SingleOrLengthOpts +): T.PsString[] { + if ("long" in r) { + // because we want to use the short shwul and past equatives as the default + if (r.long[0].p.startsWith("شول") || r.long[0].p.startsWith("ول")) { + return [...r.short, ...r.long]; } - if (Array.isArray(r)) { - return r; - } - return [r]; + return [...r.long, ...r.short, ...("mini" in r && r.mini ? r.mini : [])]; + } + return r; } - - diff --git a/src/lib/src/phrase-building/render-vp.ts b/src/lib/src/phrase-building/render-vp.ts index 397dc1b..4f74d0f 100644 --- a/src/lib/src/phrase-building/render-vp.ts +++ b/src/lib/src/phrase-building/render-vp.ts @@ -1,285 +1,339 @@ import * as T from "../../../types"; -import { - mapVerbRenderedOutput, -} from "../fmaps"; +import { mapVerbRenderedOutput } from "../fmaps"; import { removeAccents } from "../accent-helpers"; -import { - getPersonFromNP, - isPastTense, -} from "./vp-tools"; -import { - isImperativeTense, - isPattern4Entry, -} from "../type-predicates"; +import { getPersonFromNP, isPastTense } from "./vp-tools"; +import { isImperativeTense, isPattern4Entry } from "../type-predicates"; import { renderVerb } from "../new-verb-engine/render-verb"; import { renderEnglishVPBase } from "./english-vp-rendering"; import { renderNPSelection } from "./render-np"; -import { 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 { + findPossesivesToShrink, + orderKids, + getMiniPronounPs, +} from "./render-common"; import { renderComplementSelection } from "./render-complement"; -import { personToGenNum } from "../misc-helpers"; export function renderVP(VP: T.VPSelectionComplete): T.VPRendered { - const subject = getSubjectSelection(VP.blocks).selection; - const object = getObjectSelection(VP.blocks).selection; - // Sentence Rules Logic - const isPast = isPastTense(VP.verb.tense); - const isTransitive = object !== "none"; - const { king, servant } = getKingAndServant(isPast, isTransitive); - const kingPerson = getPersonFromNP( - king === "subject" ? subject : object, - ); - const complementPerson = getPersonFromNP(object ? object : subject) - // TODO: more elegant way of handling this type safety - if (kingPerson === undefined) { - throw new Error("king of sentance does not exist"); + const subject = getSubjectSelection(VP.blocks).selection; + const object = getObjectSelection(VP.blocks).selection; + // Sentence Rules Logic + const isPast = isPastTense(VP.verb.tense); + const isTransitive = object !== "none"; + const { king, servant } = getKingAndServant(isPast, isTransitive); + const kingPerson = getPersonFromNP(king === "subject" ? subject : object); + const complementPerson = getPersonFromNP(object ? object : subject); + // TODO: more elegant way of handling this type safety + if (kingPerson === undefined) { + throw new Error("king of sentance does not exist"); + } + const subjectPerson = getPersonFromNP(subject); + const objectPerson = getPersonFromNP(object); + const inflectSubject = + isPast && isTransitive && !isMascSingAnimatePattern4(subject); + const inflectObject = !isPast && isFirstOrSecondPersPronoun(object); + // Render Elements + const firstBlocks = renderVPBlocks(VP.blocks, VP.externalComplement, { + inflectSubject, + inflectObject, + king, + complementPerson, + }); + const { vbs, hasBa } = renderVerb({ + verb: VP.verb.verb, + tense: VP.verb.tense, + subject: subjectPerson, + object: objectPerson, + 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: VBwNeg.map((VBvars) => [...firstBlocks, ...VBvars]), + kids: getVPKids(hasBa, VP.blocks, VP.form, king), + englishBase: renderEnglishVPBase({ + subjectPerson, + object: VP.verb.isCompound === "dynamic" ? "none" : object, + vs: VP.verb, + }), + form: VP.form, + whatsAdjustable: whatsAdjustable(VP), + }; +} + +function getVPKids( + hasBa: boolean, + blocks: T.VPSBlockComplete[], + form: T.FormVersion, + king: "subject" | "object" +): T.Kid[] { + const subject = getSubjectSelection(blocks).selection; + const objectS = getObjectSelection(blocks).selection; + const object = typeof objectS === "object" ? objectS : undefined; + const servantNP = king === "subject" ? object : subject; + const shrunkenServant = + form.shrinkServant && servantNP + ? makeKid(shrinkServant(servantNP)) + : undefined; + const shrunkenPossesives = findPossesivesToShrink( + removeAbbreviated(blocks, form, king) + ).map(makeKid); + return orderKids([ + ...(hasBa ? [makeKid({ type: "ba" })] : []), + ...(shrunkenServant ? [shrunkenServant] : []), + ...(shrunkenPossesives ? shrunkenPossesives : []), + ]); +} + +function removeAbbreviated( + blocks: T.VPSBlockComplete[], + form: T.FormVersion, + king: "subject" | "object" +): T.VPSBlockComplete[] { + return blocks.filter(({ block }) => { + if (block.type === "subjectSelection") { + if (form.shrinkServant && king === "object") return false; + if (form.removeKing && king === "subject") return false; } - const subjectPerson = getPersonFromNP(subject); - const objectPerson = getPersonFromNP(object); - const inflectSubject = isPast && isTransitive && !isMascSingAnimatePattern4(subject); - const inflectObject = !isPast && isFirstOrSecondPersPronoun(object); - // Render Elements - const firstBlocks = renderVPBlocks(VP.blocks, VP.externalComplement, { - inflectSubject, - inflectObject, - king, - complementPerson, - }); - const { vbs, hasBa } = renderVerb({ - verb: VP.verb.verb, - tense: VP.verb.tense, - person: kingPerson, - complementGenNum: personToGenNum(objectPerson || 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: VBwNeg.map(VBvars => [...firstBlocks, ...VBvars]), - kids: getVPKids(hasBa, VP.blocks, VP.form, king), - englishBase: renderEnglishVPBase({ - subjectPerson, - object: VP.verb.isCompound === "dynamic" ? "none" : object, - vs: VP.verb, - }), - form: VP.form, - whatsAdjustable: whatsAdjustable(VP), - }; -} - -function getVPKids(hasBa: boolean, blocks: T.VPSBlockComplete[], form: T.FormVersion, king: "subject" | "object"): T.Kid[] { - const subject = getSubjectSelection(blocks).selection; - const objectS = getObjectSelection(blocks).selection; - const object = typeof objectS === "object" ? objectS : undefined; - const servantNP = king === "subject" ? object : subject; - const shrunkenServant = (form.shrinkServant && servantNP) - ? makeKid(shrinkServant(servantNP)) - : undefined; - const shrunkenPossesives = findPossesivesToShrink(removeAbbreviated(blocks, form, king)).map(makeKid); - return orderKids([ - ...hasBa ? [makeKid({ type: "ba" })] : [], - ...shrunkenServant ? [shrunkenServant] : [], - ...shrunkenPossesives ? shrunkenPossesives : [], - ]); -} - -function removeAbbreviated(blocks: T.VPSBlockComplete[], form: T.FormVersion, king: "subject" | "object"): T.VPSBlockComplete[] { - return blocks.filter(({ block }) => { - if (block.type === "subjectSelection") { - if (form.shrinkServant && king === "object") return false; - if (form.removeKing && king === "subject") return false; - } - if (block.type === "objectSelection") { - if (form.shrinkServant && king === "subject") return false; - if (form.removeKing && king === "object") return false; - } - return true; - }); -} - -function insertNegative(blocks: T.VerbRenderedOutput, negative: boolean, imperative: boolean): T.Block[][] { - if (!negative) { - return [blocks.flat().map(makeBlock)]; - }; - const blocksA = blocks.flat().map(makeBlock); - const blocksNoAccentA = mapVerbRenderedOutput(removeAccents, 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 [ - insertFromEnd(swapEndingBlocks(blocksA), neg, 2), - insertFromEnd(swapEndingBlocks(blocksA, 2), neg, 3), - insertFromEnd(blocksNoAccentA, neg, 1), - ] - } - return [ - insertFromEnd(swapEndingBlocks(blocksA), neg, 2), - insertFromEnd(blocksNoAccentA, neg, 1), - ]; + if (block.type === "objectSelection") { + if (form.shrinkServant && king === "subject") return false; + if (form.removeKing && king === "object") return false; } + return true; + }); +} + +export function insertNegative( + blocks: T.VerbRenderedOutput, + negative: boolean, + imperative: boolean +): T.Block[][] { + if (!negative) { + return [blocks.flat().map(makeBlock)]; + } + const blocksA = blocks.flat().map(makeBlock); + const blocksNoAccentA = mapVerbRenderedOutput(removeAccents, 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 [ - insertFromEnd(blocksNoAccentA, neg, 1), - insertFromEnd(blocksNoAccentA, neg, 2), - ]; - } else { - return [insertFromEnd(blocksNoAccentA, neg, 1)]; + return [ + insertFromEnd(swapEndingBlocks(blocksA), neg, 2), + insertFromEnd(swapEndingBlocks(blocksA, 2), neg, 3), + insertFromEnd(blocksNoAccentA, neg, 1), + ]; } + return [ + insertFromEnd(swapEndingBlocks(blocksA), neg, 2), + insertFromEnd(blocksNoAccentA, neg, 1), + ]; + } + if (nonStandPerfectiveSplit) { + return [ + insertFromEnd(blocksNoAccentA, neg, 1), + insertFromEnd(blocksNoAccentA, neg, 2), + ]; + } else { + return [insertFromEnd(blocksNoAccentA, neg, 1)]; + } } function swapEndingBlocks(arr: X[], n: number = 1): X[] { - return [ - ...arr.slice(0, arr.length - (n + 1)), - ...arr.slice(-n), - ...arr.slice(-(n + 1), -n), - ]; + return [ + ...arr.slice(0, arr.length - (n + 1)), + ...arr.slice(-n), + ...arr.slice(-(n + 1), -n), + ]; } 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), - ]; + 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; - } - if (ph.type !== "PH") { - return false; - } - return !["و", "وا"].includes(ph.ps.p); + if (!ph) { + return false; + } + if (ph.type !== "PH") { + return false; + } + return !["و", "وا"].includes(ph.ps.p); } function shrinkServant(np: T.NPSelection): T.MiniPronoun { - const person = getPersonFromNP(np); - return { - type: "mini-pronoun", - person, - ps: getMiniPronounPs(person), - source: "servant", - np, - }; + const person = getPersonFromNP(np); + return { + type: "mini-pronoun", + person, + ps: getMiniPronounPs(person), + source: "servant", + np, + }; } - -function renderVPBlocks(blocks: T.VPSBlockComplete[], externalComplement: T.VPSelectionComplete["externalComplement"], config: { - inflectSubject: boolean, - inflectObject: boolean, - king: "subject" | "object", - complementPerson: T.Person | undefined, -}): T.Block[] { - const object = getObjectSelection(blocks); - const subject = getSubjectSelection(blocks); - const adverbPerson = typeof object.selection === "object" - ? getPersonFromNP(object.selection) - : getPersonFromNP(subject.selection); - const b = externalComplement ? [...blocks, { block: externalComplement }] : blocks - return b.reduce((blocks, { block }): T.Block[] => { - if (block.type === "subjectSelection") { - return [ - ...blocks, - makeBlock({ - type: "subjectSelection", - selection: renderNPSelection(block.selection, config.inflectSubject, false, "subject", config.king === "subject" ? "king" : "servant", false), - }), - ]; - } - if (block.type === "objectSelection") { - const object = typeof block === "object" ? block.selection : block; - if (typeof object !== "object") { - return [ - ...blocks, - makeBlock({ - type: "objectSelection", - selection: object, - }), - ]; - } - const selection = renderNPSelection(object, config.inflectObject, true, "object", config.king === "object" ? "king" : "servant", false); - return [ - ...blocks, - makeBlock({ - type: "objectSelection", - selection, - }), - ]; - } - if (block.type === "AP") { - return [ - ...blocks, - makeBlock(renderAPSelection(block, adverbPerson ?? 0)), - ]; - } - return [ - ...blocks, - makeBlock( - renderComplementSelection( - block, - // just for typesafety // TODO: only include the person if we're doing an adjective - config.complementPerson || T.Person.FirstSingMale, - )), - ]; - }, [] as T.Block[]); -} - -function whatsAdjustable(VP: T.VPSelectionComplete): "both" | "king" | "servant" { - // TODO: intransitive dynamic compounds? - return (VP.verb.isCompound === "dynamic" && VP.verb.transitivity === "transitive") - ? (isPastTense(VP.verb.tense) ? "servant" : "king") - : VP.verb.transitivity === "transitive" - ? (VP.verb.voice === "active" ? "both" : "king") - : VP.verb.transitivity === "intransitive" - ? "king" - // grammTrans - : isPastTense(VP.verb.tense) - ? "servant" - : "king"; -} - -export function getKingAndServant(isPast: boolean, isTransitive: boolean): - { king: "subject", servant: "object" } | - { king: "object", servant: "subject" } | - { king: "subject", servant: undefined } { - if (!isTransitive) { - return { king: "subject", servant: undefined }; +function renderVPBlocks( + blocks: T.VPSBlockComplete[], + externalComplement: T.VPSelectionComplete["externalComplement"], + config: { + inflectSubject: boolean; + inflectObject: boolean; + king: "subject" | "object"; + complementPerson: T.Person | undefined; + } +): T.Block[] { + const object = getObjectSelection(blocks); + const subject = getSubjectSelection(blocks); + const adverbPerson = + typeof object.selection === "object" + ? getPersonFromNP(object.selection) + : getPersonFromNP(subject.selection); + const b = externalComplement + ? [...blocks, { block: externalComplement }] + : blocks; + return b.reduce((blocks, { block }): T.Block[] => { + if (block.type === "subjectSelection") { + return [ + ...blocks, + makeBlock({ + type: "subjectSelection", + selection: renderNPSelection( + block.selection, + config.inflectSubject, + false, + "subject", + config.king === "subject" ? "king" : "servant", + false + ), + }), + ]; } - return isPast ? { + if (block.type === "objectSelection") { + const object = typeof block === "object" ? block.selection : block; + if (typeof object !== "object") { + return [ + ...blocks, + makeBlock({ + type: "objectSelection", + selection: object, + }), + ]; + } + const selection = renderNPSelection( + object, + config.inflectObject, + true, + "object", + config.king === "object" ? "king" : "servant", + false + ); + return [ + ...blocks, + makeBlock({ + type: "objectSelection", + selection, + }), + ]; + } + if (block.type === "AP") { + return [ + ...blocks, + makeBlock(renderAPSelection(block, adverbPerson ?? 0)), + ]; + } + return [ + ...blocks, + makeBlock( + renderComplementSelection( + block, + // just for typesafety // TODO: only include the person if we're doing an adjective + config.complementPerson || T.Person.FirstSingMale + ) + ), + ]; + }, [] as T.Block[]); +} + +function whatsAdjustable( + VP: T.VPSelectionComplete +): "both" | "king" | "servant" { + // TODO: intransitive dynamic compounds? + return VP.verb.isCompound === "dynamic" && + VP.verb.transitivity === "transitive" + ? isPastTense(VP.verb.tense) + ? "servant" + : "king" + : VP.verb.transitivity === "transitive" + ? VP.verb.voice === "active" + ? "both" + : "king" + : VP.verb.transitivity === "intransitive" + ? "king" + : // grammTrans + isPastTense(VP.verb.tense) + ? "servant" + : "king"; +} + +export function getKingAndServant( + isPast: boolean, + isTransitive: boolean +): + | { king: "subject"; servant: "object" } + | { king: "object"; servant: "subject" } + | { king: "subject"; servant: undefined } { + if (!isTransitive) { + return { king: "subject", servant: undefined }; + } + return isPast + ? { king: "object", servant: "subject", - } : { + } + : { king: "subject", servant: "object", - }; + }; } -function isFirstOrSecondPersPronoun(o: "none" | T.NPSelection | T.Person.ThirdPlurMale): boolean { - if (typeof o !== "object") return false; - if (o.selection.type !== "pronoun") return false; - return [0,1,2,3,6,7,8,9].includes(o.selection.person); +function isFirstOrSecondPersPronoun( + o: "none" | T.NPSelection | T.Person.ThirdPlurMale +): boolean { + if (typeof o !== "object") return false; + if (o.selection.type !== "pronoun") return false; + return [0, 1, 2, 3, 6, 7, 8, 9].includes(o.selection.person); } function isMascSingAnimatePattern4(np: T.NPSelection): boolean { - if (np.selection.type !== "noun") { - return false; - } - return isPattern4Entry(np.selection.entry) - && np.selection.entry.c.includes("anim.") - && (np.selection.number === "singular") - && (np.selection.gender === "masc"); + if (np.selection.type !== "noun") { + return false; + } + return ( + isPattern4Entry(np.selection.entry) && + np.selection.entry.c.includes("anim.") && + np.selection.number === "singular" && + np.selection.gender === "masc" + ); } diff --git a/src/lib/src/phrase-building/vp-tools.ts b/src/lib/src/phrase-building/vp-tools.ts index 4930055..e32634e 100644 --- a/src/lib/src/phrase-building/vp-tools.ts +++ b/src/lib/src/phrase-building/vp-tools.ts @@ -1,338 +1,361 @@ import * as T from "../../../types"; -import { - concatPsString, - psRemove, - psStringEquals, -} from "../p-text-helpers"; -import { isImperativeTense, isPerfectTense } from "../type-predicates"; +import { concatPsString, psRemove, psStringEquals } from "../p-text-helpers"; +import { isPerfectTense } from "../type-predicates"; import * as grammarUnits from "../grammar-units"; import { isSecondPerson, randomNumber } from "../misc-helpers"; -import { adjustObjectSelection, adjustSubjectSelection, getObjectSelection, getSubjectSelection, VPSBlocksAreComplete } from "./blocks-utils"; +import { + adjustObjectSelection, + adjustSubjectSelection, + getObjectSelection, + getSubjectSelection, + VPSBlocksAreComplete, +} from "./blocks-utils"; export function isInvalidSubjObjCombo(subj: T.Person, obj: T.Person): boolean { - const firstPeople = [ - T.Person.FirstSingMale, - T.Person.FirstSingFemale, - T.Person.FirstPlurMale, - T.Person.FirstPlurFemale, - ]; - const secondPeople = [ - T.Person.SecondSingMale, - T.Person.SecondSingFemale, - T.Person.SecondPlurMale, - T.Person.SecondPlurFemale, - ]; - return ( - (firstPeople.includes(subj) && firstPeople.includes(obj)) - || - (secondPeople.includes(subj) && secondPeople.includes(obj)) - ); + const firstPeople = [ + T.Person.FirstSingMale, + T.Person.FirstSingFemale, + T.Person.FirstPlurMale, + T.Person.FirstPlurFemale, + ]; + const secondPeople = [ + T.Person.SecondSingMale, + T.Person.SecondSingFemale, + T.Person.SecondPlurMale, + T.Person.SecondPlurFemale, + ]; + return ( + (firstPeople.includes(subj) && firstPeople.includes(obj)) || + (secondPeople.includes(subj) && secondPeople.includes(obj)) + ); } -/** - * @param conjR - * @param tense - * @param voice - * @param negative - * @returns - */ -export function getTenseVerbForm( - conjR: T.VerbConjugation, - tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense, - voice: "active" | "passive", - mode: "charts" | "phrase-building", - negative: boolean, -): T.VerbForm | T.ImperativeForm { - const conj = (voice === "passive" && conjR.passive) ? conjR.passive : conjR; - if (isImperativeTense(tense)) { - const impPassError = new Error("can't use imperative tenses with passive voice") - if (voice === "passive") { - throw impPassError; - } - if (!conj.imperfective.imperative || !conj.perfective.imperative) throw impPassError; - // charts can't display negative form - return (tense === "perfectiveImperative" && (!negative || mode === "charts")) - ? conj.perfective.imperative - : conj.imperfective.imperative; - } - if (tense === "presentVerb") { - return conj.imperfective.nonImperative; - } - if (tense === "subjunctiveVerb") { - return conj.perfective.nonImperative; - } - if (tense === "imperfectiveFuture") { - return conj.imperfective.future; - } - if (tense === "perfectiveFuture") { - return conj.perfective.future; - } - if (tense === "imperfectivePast") { - return conj.imperfective.past; - } - if (tense === "perfectivePast") { - return conj.perfective.past; - } - if (tense === "habitualImperfectivePast") { - return conj.imperfective.habitualPast; - } - if (tense === "habitualPerfectivePast") { - return conj.perfective.habitualPast; - } - if (tense === "presentVerbModal") { - return conj.imperfective.modal.nonImperative; - } - if (tense === "subjunctiveVerbModal") { - return conj.perfective.modal.nonImperative; - } - if (tense === "imperfectiveFutureModal") { - return conj.imperfective.modal.future; - } - if (tense === "perfectiveFutureModal") { - return conj.perfective.modal.future; - } - if (tense === "imperfectivePastModal") { - return conj.imperfective.modal.past; - } - if (tense === "perfectivePastModal") { - return conj.perfective.modal.past; - } - if (tense === "habitualImperfectivePastModal") { - return conj.imperfective.modal.habitualPast; - } - if (tense === "habitualPerfectivePastModal") { - return conj.perfective.modal.habitualPast; - } - if (tense === "presentPerfect") { - return conj.perfect.present; - } - if (tense === "pastPerfect") { - return conj.perfect.past; - } - if (tense === "futurePerfect") { - return conj.perfect.future; - } - if (tense === "habitualPerfect") { - return conj.perfect.habitual; - } - if (tense === "subjunctivePerfect") { - return conj.perfect.subjunctive; - } - if (tense === "wouldBePerfect") { - return conj.perfect.wouldBe; - } - if (tense === "pastSubjunctivePerfect") { - return conj.perfect.pastSubjunctive; - } - if (tense === "wouldHaveBeenPerfect") { - return conj.perfect.wouldHaveBeen; - } - throw new Error("unknown tense"); -} +// DEPRECATED +// /** +// * @param conjR +// * @param tense +// * @param voice +// * @param negative +// * @returns +// */ +// export function getTenseVerbForm( +// conjR: T.VerbConjugation, +// tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense, +// voice: "active" | "passive", +// mode: "charts" | "phrase-building", +// negative: boolean, +// ): T.VerbForm | T.ImperativeForm { +// const conj = (voice === "passive" && conjR.passive) ? conjR.passive : conjR; +// if (isImperativeTense(tense)) { +// const impPassError = new Error("can't use imperative tenses with passive voice") +// if (voice === "passive") { +// throw impPassError; +// } +// if (!conj.imperfective.imperative || !conj.perfective.imperative) throw impPassError; +// // charts can't display negative form +// return (tense === "perfectiveImperative" && (!negative || mode === "charts")) +// ? conj.perfective.imperative +// : conj.imperfective.imperative; +// } +// if (tense === "presentVerb") { +// return conj.imperfective.nonImperative; +// } +// if (tense === "subjunctiveVerb") { +// return conj.perfective.nonImperative; +// } +// if (tense === "imperfectiveFuture") { +// return conj.imperfective.future; +// } +// if (tense === "perfectiveFuture") { +// return conj.perfective.future; +// } +// if (tense === "imperfectivePast") { +// return conj.imperfective.past; +// } +// if (tense === "perfectivePast") { +// return conj.perfective.past; +// } +// if (tense === "habitualImperfectivePast") { +// return conj.imperfective.habitualPast; +// } +// if (tense === "habitualPerfectivePast") { +// return conj.perfective.habitualPast; +// } +// if (tense === "presentVerbModal") { +// return conj.imperfective.modal.nonImperative; +// } +// if (tense === "subjunctiveVerbModal") { +// return conj.perfective.modal.nonImperative; +// } +// if (tense === "imperfectiveFutureModal") { +// return conj.imperfective.modal.future; +// } +// if (tense === "perfectiveFutureModal") { +// return conj.perfective.modal.future; +// } +// if (tense === "imperfectivePastModal") { +// return conj.imperfective.modal.past; +// } +// if (tense === "perfectivePastModal") { +// return conj.perfective.modal.past; +// } +// if (tense === "habitualImperfectivePastModal") { +// return conj.imperfective.modal.habitualPast; +// } +// if (tense === "habitualPerfectivePastModal") { +// return conj.perfective.modal.habitualPast; +// } +// if (tense === "presentPerfect") { +// return conj.perfect.present; +// } +// if (tense === "pastPerfect") { +// return conj.perfect.past; +// } +// if (tense === "futurePerfect") { +// return conj.perfect.future; +// } +// if (tense === "habitualPerfect") { +// return conj.perfect.habitual; +// } +// if (tense === "subjunctivePerfect") { +// return conj.perfect.subjunctive; +// } +// if (tense === "wouldBePerfect") { +// return conj.perfect.wouldBe; +// } +// if (tense === "pastSubjunctivePerfect") { +// return conj.perfect.pastSubjunctive; +// } +// if (tense === "wouldHaveBeenPerfect") { +// return conj.perfect.wouldHaveBeen; +// } +// throw new Error("unknown tense"); +// } export function getPersonFromNP(np: T.NPSelection): T.Person; -export function getPersonFromNP(np: T.NPSelection | T.ObjectNP): T.Person | undefined; -export function getPersonFromNP(np: T.NPSelection | T.ObjectNP): T.Person | undefined { - if (np === "none") { - return undefined; - } - if (typeof np === "number") return np; - if (np.selection.type === "participle") { - return T.Person.ThirdPlurMale; - } - if (np.selection.type === "pronoun") { - return np.selection.person; - } - return np.selection.number === "plural" - ? (np.selection.gender === "masc" ? T.Person.ThirdPlurMale : T.Person.ThirdPlurFemale) - : (np.selection.gender === "masc" ? T.Person.ThirdSingMale : T.Person.ThirdSingFemale); +export function getPersonFromNP( + np: T.NPSelection | T.ObjectNP +): T.Person | undefined; +export function getPersonFromNP( + np: T.NPSelection | T.ObjectNP +): T.Person | undefined { + if (np === "none") { + return undefined; + } + if (typeof np === "number") return np; + if (np.selection.type === "participle") { + return T.Person.ThirdPlurMale; + } + if (np.selection.type === "pronoun") { + return np.selection.person; + } + return np.selection.number === "plural" + ? np.selection.gender === "masc" + ? T.Person.ThirdPlurMale + : T.Person.ThirdPlurFemale + : np.selection.gender === "masc" + ? T.Person.ThirdSingMale + : T.Person.ThirdSingFemale; } export function removeBa(ps: T.PsString): T.PsString { - return psRemove(ps, concatPsString(grammarUnits.baParticle, " ")); + return psRemove(ps, concatPsString(grammarUnits.baParticle, " ")); } -export function getTenseFromVerbSelection(vs: T.VerbSelection): T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense { - function verbTenseToModalTense(tn: T.VerbTense): T.AbilityTense { - if (tn === "presentVerb") { - return "presentVerbModal"; - } - if (tn === "subjunctiveVerb") { - return "subjunctiveVerbModal"; - } - if (tn === "imperfectiveFuture") { - return "imperfectiveFutureModal"; - } - if (tn === "perfectiveFuture") { - return "perfectiveFutureModal"; - } - if (tn === "perfectivePast") { - return "perfectivePastModal"; - } - if (tn === "imperfectivePast") { - return "imperfectivePastModal"; - } - if (tn === "habitualImperfectivePast") { - return "habitualImperfectivePastModal"; - } - if (tn === "habitualPerfectivePast") { - return "habitualPerfectivePastModal"; - } - throw new Error("can't convert non verbTense to modalTense"); +export function getTenseFromVerbSelection( + vs: T.VerbSelection +): T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense { + function verbTenseToModalTense(tn: T.VerbTense): T.AbilityTense { + if (tn === "presentVerb") { + return "presentVerbModal"; } - if (vs.tenseCategory === "basic") { - return vs.verbTense; + if (tn === "subjunctiveVerb") { + return "subjunctiveVerbModal"; } - if (vs.tenseCategory === "perfect") { - return vs.perfectTense; + if (tn === "imperfectiveFuture") { + return "imperfectiveFutureModal"; } - if (vs.tenseCategory === "imperative") { - return vs.imperativeTense; + if (tn === "perfectiveFuture") { + return "perfectiveFutureModal"; } - // vs.tenseCategory === "modal" - return verbTenseToModalTense(vs.verbTense); + if (tn === "perfectivePast") { + return "perfectivePastModal"; + } + if (tn === "imperfectivePast") { + return "imperfectivePastModal"; + } + if (tn === "habitualImperfectivePast") { + return "habitualImperfectivePastModal"; + } + if (tn === "habitualPerfectivePast") { + return "habitualPerfectivePastModal"; + } + throw new Error("can't convert non verbTense to modalTense"); + } + if (vs.tenseCategory === "basic") { + return vs.verbTense; + } + if (vs.tenseCategory === "perfect") { + return vs.perfectTense; + } + if (vs.tenseCategory === "imperative") { + return vs.imperativeTense; + } + // vs.tenseCategory === "modal" + return verbTenseToModalTense(vs.verbTense); } export function isPastTense(tense: T.Tense): boolean { - if (isPerfectTense(tense)) return true; - return tense.toLowerCase().includes("past"); + if (isPerfectTense(tense)) return true; + return tense.toLowerCase().includes("past"); } export function perfectTenseHasBa(tense: T.PerfectTense): boolean { - const withBa: Parameters[0][] = [ - "futurePerfect", - "wouldBePerfect", - "wouldHaveBeenPerfect", - ]; - return withBa.includes(tense); + const withBa: Parameters[0][] = [ + "futurePerfect", + "wouldBePerfect", + "wouldHaveBeenPerfect", + ]; + return withBa.includes(tense); } export function removeDuplicates(psv: T.PsString[]): T.PsString[] { - return psv.filter((ps, i, arr) => ( - i === arr.findIndex(t => ( - psStringEquals(t, ps) - )) - )); + return psv.filter( + (ps, i, arr) => i === arr.findIndex((t) => psStringEquals(t, ps)) + ); } -export function switchSubjObj(vps: V): V { - const subject = getSubjectSelection(vps.blocks).selection; - const object = getObjectSelection(vps.blocks).selection; - if ("tenseCategory" in vps.verb) { - if (!subject || !(typeof object === "object") || (vps.verb.tenseCategory === "imperative")) { - return vps; - } - return { - ...vps, - blocks: adjustObjectSelection( - adjustSubjectSelection(vps.blocks, object), - subject, - ), - }; - } - if (!subject|| !vps.verb || !(typeof object === "object")) { - return vps; +export function switchSubjObj< + V extends T.VPSelectionState | T.VPSelectionComplete +>(vps: V): V { + const subject = getSubjectSelection(vps.blocks).selection; + const object = getObjectSelection(vps.blocks).selection; + if ("tenseCategory" in vps.verb) { + if ( + !subject || + !(typeof object === "object") || + vps.verb.tenseCategory === "imperative" + ) { + return vps; } return { - ...vps, - blocks: adjustObjectSelection( - adjustSubjectSelection(vps.blocks, object), - subject, - ), + ...vps, + blocks: adjustObjectSelection( + adjustSubjectSelection(vps.blocks, object), + subject + ), }; + } + if (!subject || !vps.verb || !(typeof object === "object")) { + return vps; + } + return { + ...vps, + blocks: adjustObjectSelection( + adjustSubjectSelection(vps.blocks, object), + subject + ), + }; } -export function completeVPSelection(vps: T.VPSelectionState): T.VPSelectionComplete | undefined { - if (!VPSBlocksAreComplete(vps.blocks)) { - return undefined; - } - // necessary for this version on typscript ... - const verb: T.VerbSelectionComplete = { - ...vps.verb, - tense: getTenseFromVerbSelection(vps.verb), - }; - return { - ...vps, - verb, - blocks: vps.blocks, - }; +export function completeVPSelection( + vps: T.VPSelectionState +): T.VPSelectionComplete | undefined { + if (!VPSBlocksAreComplete(vps.blocks)) { + return undefined; + } + // necessary for this version on typscript ... + const verb: T.VerbSelectionComplete = { + ...vps.verb, + tense: getTenseFromVerbSelection(vps.verb), + }; + return { + ...vps, + verb, + blocks: vps.blocks, + }; } export function isThirdPerson(p: T.Person): boolean { - return ( - p === T.Person.ThirdSingMale || - p === T.Person.ThirdSingFemale || - p === T.Person.ThirdPlurMale || - p === T.Person.ThirdPlurFemale - ); + return ( + p === T.Person.ThirdSingMale || + p === T.Person.ThirdSingFemale || + p === T.Person.ThirdPlurMale || + p === T.Person.ThirdPlurFemale + ); } -export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState): T.VPSelectionState { - const subject = getSubjectSelection(vps.blocks).selection; - const object = getObjectSelection(vps.blocks).selection; - const subjIs2ndPerson = (subject?.selection.type === "pronoun") && isSecondPerson(subject.selection.person); - const objIs2ndPerson = (typeof object === "object") - && (object.selection.type === "pronoun") - && isSecondPerson(object.selection.person); - const default2ndPersSubject: T.NPSelection = { +export function ensure2ndPersSubjPronounAndNoConflict( + vps: T.VPSelectionState +): T.VPSelectionState { + const subject = getSubjectSelection(vps.blocks).selection; + const object = getObjectSelection(vps.blocks).selection; + const subjIs2ndPerson = + subject?.selection.type === "pronoun" && + isSecondPerson(subject.selection.person); + const objIs2ndPerson = + typeof object === "object" && + object.selection.type === "pronoun" && + isSecondPerson(object.selection.person); + const default2ndPersSubject: T.NPSelection = { + type: "NP", + selection: { + type: "pronoun", + distance: "far", + person: T.Person.SecondSingMale, + }, + }; + function getNon2ndPersPronoun() { + let newObjPerson: T.Person; + do { + newObjPerson = randomNumber(0, 12); + } while (isSecondPerson(newObjPerson)); + return newObjPerson; + } + if (subjIs2ndPerson && !objIs2ndPerson) { + return vps; + } + if (subjIs2ndPerson && objIs2ndPerson) { + if (typeof object !== "object" || object.selection.type !== "pronoun") { + return vps; + } + return { + ...vps, + blocks: adjustObjectSelection(vps.blocks, { type: "NP", selection: { - type: "pronoun", - distance: "far", - person: T.Person.SecondSingMale, + ...object.selection, + person: getNon2ndPersPronoun(), }, + }), }; - function getNon2ndPersPronoun() { - let newObjPerson: T.Person; - do { - newObjPerson = randomNumber(0, 12); - } while(isSecondPerson(newObjPerson)); - return newObjPerson; + } + if (!subjIs2ndPerson && objIs2ndPerson) { + if (typeof object !== "object" || object.selection.type !== "pronoun") { + return { + ...vps, + blocks: adjustSubjectSelection(vps.blocks, default2ndPersSubject), + }; } - if (subjIs2ndPerson && !objIs2ndPerson) { - return vps; - } - if (subjIs2ndPerson && objIs2ndPerson) { - if (typeof object !== "object" || object.selection.type !== "pronoun") { - return vps; + return { + ...vps, + blocks: adjustObjectSelection( + adjustSubjectSelection(vps.blocks, default2ndPersSubject), + { + type: "NP", + selection: { + ...object.selection, + person: getNon2ndPersPronoun(), + }, } - return { - ...vps, - blocks: adjustObjectSelection(vps.blocks, { - type: "NP", - selection: { - ...object.selection, - person: getNon2ndPersPronoun(), - }, - }), - }; - } - if (!subjIs2ndPerson && objIs2ndPerson) { - if (typeof object !== "object" || object.selection.type !== "pronoun") { - return { - ...vps, - blocks: adjustSubjectSelection(vps.blocks, default2ndPersSubject) - }; - } - return { - ...vps, - blocks: adjustObjectSelection( - adjustSubjectSelection(vps.blocks, default2ndPersSubject), - { - type: "NP", - selection: { - ...object.selection, - person: getNon2ndPersPronoun(), - }, - }, - ), - }; - } - if (!subjIs2ndPerson && !objIs2ndPerson) { - return { - ...vps, - blocks: adjustSubjectSelection(vps.blocks, default2ndPersSubject), - }; - } - throw new Error("error ensuring compatible VPSelection for imperative verb"); -} \ No newline at end of file + ), + }; + } + if (!subjIs2ndPerson && !objIs2ndPerson) { + return { + ...vps, + blocks: adjustSubjectSelection(vps.blocks, default2ndPersSubject), + }; + } + throw new Error("error ensuring compatible VPSelection for imperative verb"); +} diff --git a/src/lib/src/verb-info.ts b/src/lib/src/verb-info.ts index 9309bcb..6e84516 100644 --- a/src/lib/src/verb-info.ts +++ b/src/lib/src/verb-info.ts @@ -7,55 +7,44 @@ */ import { - concatPsString, - psStringEquals, - removeEndingL, - yulEndingInfinitive, - removeRetroflexR, - unisexInfToObjectMatrix, - complementInflects, - beginsWithDirectionalPronoun, - checkForOoPrefix, - removeStartingTick, - ensureShortWurShwaShift, - choosePersInf, - isUnisexSet, - getLong, - getShort, + concatPsString, + psStringEquals, + removeEndingL, + yulEndingInfinitive, + removeRetroflexR, + unisexInfToObjectMatrix, + complementInflects, + beginsWithDirectionalPronoun, + checkForOoPrefix, + removeStartingTick, + ensureShortWurShwaShift, + choosePersInf, + isUnisexSet, + getLong, + getShort, } from "./p-text-helpers"; +import { makePsString, removeFVarients } from "./accent-and-ps-utils"; +import { inflectYey } from "./pashto-inflector"; import { - makePsString, - removeFVarients, -} from "./accent-and-ps-utils"; -import { - inflectYey, -} from "./pashto-inflector"; -import { - accentOnFront, - accentOnNFromEnd, - removeAccents, + accentOnFront, + accentOnNFromEnd, + removeAccents, } from "./accent-helpers"; +import { inflectWord } from "./pashto-inflector"; import { - inflectWord, -} from "./pashto-inflector"; -import { - checkForIrregularConjugation, - kedulStat, - stativeAux, + checkForIrregularConjugation, + kedulStat, + stativeAux, } from "./irregular-conjugations"; +import { presentParticipleSuffix } from "./grammar-units"; +import { dynamicAuxVerbs } from "./dyn-comp-aux-verbs"; import { - presentParticipleSuffix -} from "./grammar-units"; -import { - dynamicAuxVerbs, -} from "./dyn-comp-aux-verbs"; -import { - getPersonInflectionsKey, - getPersonNumber, - spaceInForm, - getAuxTransitivity, - chooseParticipleInflection, - noPersInfs, + getPersonInflectionsKey, + getPersonNumber, + spaceInForm, + getAuxTransitivity, + chooseParticipleInflection, + noPersInfs, } from "./misc-helpers"; import * as T from "../../types"; import { isTlulVerb } from "./type-predicates"; @@ -66,1170 +55,1586 @@ const eyEndingUnaccented: T.PsString = { p: "ی", f: "ey" }; * Compiles the base information (roots, stems etc.) needed in order * to make all the conjugations. This is the first step of creating * the conjugations. - * + * * @param entry - the dictionary entry for the verb * @param complement - the dictioanry entry for the complement of the verb if compound */ export function getVerbInfo( - ent: T.DictionaryEntry, - complmnt?: T.DictionaryEntry, + ent: T.DictionaryEntry, + complmnt?: T.DictionaryEntry ): T.VerbInfo { - const entry = removeFVarients(ent); - const irregularConj = checkForIrregularConjugation(entry); - if (irregularConj) { - return irregularConj.info; - } - const complement = (complmnt && ent.c?.includes("comp.")) ? removeFVarients(complmnt) : undefined; - const type = getType(entry); - if (type === "transitive or grammatically transitive simple") { - return { - type: "transitive or grammatically transitive simple", - transitive: getVerbInfo( - // @ts-ignore (will have entry.c) - { ...entry, c: entry.c.replace("trans./gramm. trans.", "trans.") }, - ) as T.SimpleVerbInfo, - grammaticallyTransitive: getVerbInfo( - // @ts-ignore (will have entry.c) - { ...entry, c: entry.c.replace("trans./gramm. trans.", "gramm. trans.") }, - ) as T.SimpleVerbInfo, - }; - } - const transitivity = getTransitivity(entry); - if (type !== "simple") { - if (!complement) { - throw new Error("complement required for compound verb"); - } - if (type === "dynamic compound") { - return getDynamicCompoundInfo(entry, complement) - } - if (type === "dynamic or stative compound") { - return { - type: "dynamic or stative compound", - transitivity, - dynamic: getDynamicCompoundInfo( - // @ts-ignore (will have entry.c) - { ...entry, c: entry.c.replace("dyn./stat.", "dyn.") }, - complement, - ), - stative: getVerbInfo( - // @ts-ignore (will have entry.c) - { ...entry, c: entry.c.replace("dyn./stat.", "stat.") }, - complement, - ) as T.StativeCompoundVerbInfo, - }; - } - if (type === "dynamic or generative stative compound") { - return { - type: "dynamic or generative stative compound", - transitivity, - dynamic: getDynamicCompoundInfo( - // @ts-ignore (will have entry.c) - { ...entry, c: entry.c.replace("gen. stat./dyn.", "dyn.") }, - complement, - ), - stative: getGenerativeStativeCompoundVerbInfo( - // @ts-ignore (will have entry.c) - { ...entry, c: entry.c.replace("gen. stat./dyn.", "gen. stat.") }, - complement, - ), - } - } - if (type === "generative stative compound") { - return getGenerativeStativeCompoundVerbInfo(entry, complement as T.DictionaryEntryNoFVars); - } - } - const comp = complement ? ensureUnisexInf(complement) : undefined; - const root = getVerbRoots(entry, transitivity, comp); - const stem = getVerbStems(entry, root, transitivity, comp); - const infinitive = "mascSing" in root.imperfective ? root.imperfective.mascSing.long : root.imperfective.long; - const yulEnding = yulEndingInfinitive(infinitive); - const participle = getParticiple(entry, stem, infinitive, transitivity, comp); - const idiosyncraticThirdMascSing = getIdiosyncraticThirdMascSing(entry); - const baseInfo: T.VerbInfoBase = { - entry: { - // TODO: cleanup the type safety with the DictionaryNoFVars messing things up etc - entry: entry as unknown as T.VerbDictionaryEntry, - complement, - }, - transitivity, - yulEnding, - root, - stem, - participle, - ...idiosyncraticThirdMascSing ? { - idiosyncraticThirdMascSing, - } : {}, - }; - if (type === "stative compound") { - return { - ...baseInfo, - type, - complement: comp as T.UnisexInflections, - }; - } + const entry = removeFVarients(ent); + const irregularConj = checkForIrregularConjugation(entry); + if (irregularConj) { + return irregularConj.info; + } + const complement = + complmnt && ent.c?.includes("comp.") + ? removeFVarients(complmnt) + : undefined; + const type = getType(entry); + if (type === "transitive or grammatically transitive simple") { return { - ...baseInfo, - type, + type: "transitive or grammatically transitive simple", + transitive: getVerbInfo( + // @ts-ignore (will have entry.c) + { ...entry, c: entry.c.replace("trans./gramm. trans.", "trans.") } + ) as T.SimpleVerbInfo, + grammaticallyTransitive: getVerbInfo({ + ...entry, + // @ts-ignore (will have entry.c) + c: entry.c.replace("trans./gramm. trans.", "gramm. trans."), + }) as T.SimpleVerbInfo, }; + } + const transitivity = getTransitivity(entry); + if (type !== "simple") { + if (!complement) { + throw new Error("complement required for compound verb"); + } + if (type === "dynamic compound") { + return getDynamicCompoundInfo(entry, complement); + } + if (type === "dynamic or stative compound") { + return { + type: "dynamic or stative compound", + transitivity, + dynamic: getDynamicCompoundInfo( + // @ts-ignore (will have entry.c) + { ...entry, c: entry.c.replace("dyn./stat.", "dyn.") }, + complement + ), + stative: getVerbInfo( + // @ts-ignore (will have entry.c) + { ...entry, c: entry.c.replace("dyn./stat.", "stat.") }, + complement + ) as T.StativeCompoundVerbInfo, + }; + } + if (type === "dynamic or generative stative compound") { + return { + type: "dynamic or generative stative compound", + transitivity, + dynamic: getDynamicCompoundInfo( + // @ts-ignore (will have entry.c) + { ...entry, c: entry.c.replace("gen. stat./dyn.", "dyn.") }, + complement + ), + stative: getGenerativeStativeCompoundVerbInfo( + // @ts-ignore (will have entry.c) + { ...entry, c: entry.c.replace("gen. stat./dyn.", "gen. stat.") }, + complement + ), + }; + } + if (type === "generative stative compound") { + return getGenerativeStativeCompoundVerbInfo( + entry, + complement as T.DictionaryEntryNoFVars + ); + } + } + const comp = complement ? ensureUnisexInf(complement) : undefined; + const root = getVerbRoots(entry, transitivity, comp); + const stem = getVerbStems(entry, root, transitivity, comp); + const infinitive = + "mascSing" in root.imperfective + ? root.imperfective.mascSing.long + : root.imperfective.long; + const yulEnding = yulEndingInfinitive(infinitive); + const participle = getParticiple(entry, stem, infinitive, transitivity, comp); + const idiosyncraticThirdMascSing = getIdiosyncraticThirdMascSing(entry); + const baseInfo: T.VerbInfoBase = { + entry: { + // TODO: cleanup the type safety with the DictionaryNoFVars messing things up etc + entry: entry as unknown as T.VerbDictionaryEntry, + complement, + }, + transitivity, + yulEnding, + root, + stem, + participle, + ...(idiosyncraticThirdMascSing + ? { + idiosyncraticThirdMascSing, + } + : {}), + }; + if (type === "stative compound") { + return { + ...baseInfo, + type, + complement: comp as T.UnisexInflections, + }; + } + return { + ...baseInfo, + type, + }; } type Bases = { - stem: { - imperfective: T.FullForm, - perfective: T.FullForm, - perfectiveSplit?: T.SplitInfo, - }, - root: { - imperfective: T.FullForm, - perfective: T.FullForm, - perfectiveSplit?: T.SplitInfo, - }, - participle: { - present: T.FullForm, - past: T.FullForm, - }, -} + stem: { + imperfective: T.FullForm; + perfective: T.FullForm; + perfectiveSplit?: T.SplitInfo; + }; + root: { + imperfective: T.FullForm; + perfective: T.FullForm; + perfectiveSplit?: T.SplitInfo; + }; + participle: { + present: T.FullForm; + past: T.FullForm; + }; +}; function getGenerativeStativeCompoundVerbInfo( - entry: T.DictionaryEntryNoFVars, comp: T.DictionaryEntryNoFVars, forceSingular?: true, + entry: T.DictionaryEntryNoFVars, + comp: T.DictionaryEntryNoFVars, + forceSingular?: true ): T.GenerativeStativeCompoundVerbInfo { - const transitivity = getTransitivity(entry); - const transitivityNoGrammTrans = transitivity === "grammatically transitive" ? "transitive" : transitivity; - const yulEnding = null; - const objComplement = getObjComplementInfo(entry, comp, forceSingular); - const auxVerb = stativeAux[transitivityNoGrammTrans]; - const compUsed = objComplement.plural ? objComplement.plural : removeFVarients(objComplement.entry); - const bases: Bases = { - stem: { - imperfective: auxVerb.info.stem.imperfective, - perfective: auxVerb.info.stem.perfective, - }, - root: { - imperfective: auxVerb.info.root.imperfective, - perfective: auxVerb.info.root.perfective, - }, - participle: { - present: auxVerb.info.participle.present, - past: chooseParticipleInflection( - inflectYey( - "mascSing" in auxVerb.info.participle.past - // purely for type saftey, will not have mascSing - // in a non stative compound verb - /* istanbul ignore next */ - ? auxVerb.info.participle.past.mascSing - : auxVerb.info.participle.past - ), - objComplement.person, - ), - } - } - const perfectiveStem = concatPsString(compUsed, " ", bases.stem.perfective); - const stem = { - imperfective: concatPsString(compUsed, " ", bases.stem.imperfective), - perfective: perfectiveStem, - perfectiveSplit: splitPerfective(perfectiveStem, 0, 0, true), - }; - const perfectiveRoot = concatPsString(compUsed, " ", bases.root.perfective) as T.OptionalPersonInflections>; - const root = { - imperfective: concatPsString(compUsed, " ", bases.root.imperfective) as T.OptionalPersonInflections>, - perfective: perfectiveRoot, - perfectiveSplit: splitPerfective(perfectiveRoot, 0, 0, true), - }; - const participle = { - present: concatPsString(compUsed, " ", auxVerb.info.participle.present), - past: concatPsString(compUsed, " ", bases.participle.past), - } - return { - entry: { - // TODO: cleanup the type safety with the DictionaryNoFVars messing things up etc - entry: entry as unknown as T.VerbDictionaryEntry, - complement: comp, - }, - type: "generative stative compound", - transitivity, - yulEnding, - stem, - root, - participle, - objComplement, - ...objComplement.plural ? { - singularForm: getGenerativeStativeCompoundVerbInfo(entry, comp, true), - } : {}, - }; -} - -function getDynamicCompoundInfo(entry: T.DictionaryEntryNoFVars, comp: T.DictionaryEntryNoFVars, forceSingular?: true): T.DynamicCompoundVerbInfo { - const transitivity = getTransitivity(entry); - const yulEnding = null; - const objComplement = getObjComplementInfo(entry, comp, forceSingular); - const auxVerb = getDynamicAuxVerb(entry); - const auxVerbInfo = getVerbInfo(auxVerb.entry, auxVerb.complement) as T.NonComboVerbInfo; - const compUsed = objComplement.plural ? objComplement.plural : objComplement.entry; - const bases: Bases = (auxVerbInfo.type === "stative compound") - ? getObjectMatchingBases(auxVerbInfo, objComplement.person) - : { - stem: { - imperfective: auxVerbInfo.stem.imperfective, - perfective: auxVerbInfo.stem.perfective, - ...auxVerbInfo.stem.perfectiveSplit ? { - perfectiveSplit: auxVerbInfo.stem.perfectiveSplit, - } : {}, - }, - root: { - imperfective: auxVerbInfo.root.imperfective, - perfective: auxVerbInfo.root.perfective, - ...auxVerbInfo.root.perfectiveSplit ? { - perfectiveSplit: auxVerbInfo.root.perfectiveSplit, - } : {}, - }, - participle: { - present: auxVerbInfo.participle.present, - past: chooseParticipleInflection( - inflectYey( - "mascSing" in auxVerbInfo.participle.past - // purely for type saftey, will not have mascSing - // in a non stative compound verb - /* istanbul ignore next */ - ? auxVerbInfo.participle.past.mascSing - : auxVerbInfo.participle.past - ), - objComplement.person, - ), - } - } - const stem = { - imperfective: concatPsString(compUsed, " ", bases.stem.imperfective), - perfective: concatPsString(compUsed, " ", bases.stem.perfective), - ...bases.stem.perfectiveSplit ? { - perfectiveSplit: makeDynamicPerfectiveSplit(compUsed, bases.stem.perfectiveSplit), - } : {}, - }; - const root = { - imperfective: concatPsString(compUsed, " ", bases.root.imperfective) as T.OptionalPersonInflections>, - perfective: concatPsString(compUsed, " ", bases.root.perfective) as T.OptionalPersonInflections>, - ...bases.root.perfectiveSplit ? { - perfectiveSplit: makeDynamicPerfectiveSplit(compUsed, bases.root.perfectiveSplit), - } : {}, - }; - const participle = { - present: concatPsString(compUsed, " ", auxVerbInfo.participle.present), - past: concatPsString(compUsed, " ", bases.participle.past), - }; - const makeIntransitiveFormOfEntry = (e: T.DictionaryEntryNoFVars): T.DictionaryEntryNoFVars => ({ - ...e, - p: e.p.replace( - "کول", - "کېدل" + const transitivity = getTransitivity(entry); + const transitivityNoGrammTrans = + transitivity === "grammatically transitive" ? "transitive" : transitivity; + const yulEnding = null; + const objComplement = getObjComplementInfo(entry, comp, forceSingular); + const auxVerb = stativeAux[transitivityNoGrammTrans]; + const compUsed = objComplement.plural + ? objComplement.plural + : removeFVarients(objComplement.entry); + const bases: Bases = { + stem: { + imperfective: auxVerb.info.stem.imperfective, + perfective: auxVerb.info.stem.perfective, + }, + root: { + imperfective: auxVerb.info.root.imperfective, + perfective: auxVerb.info.root.perfective, + }, + participle: { + present: auxVerb.info.participle.present, + past: chooseParticipleInflection( + inflectYey( + "mascSing" in auxVerb.info.participle.past + ? // purely for type saftey, will not have mascSing + // in a non stative compound verb + /* istanbul ignore next */ + auxVerb.info.participle.past.mascSing + : auxVerb.info.participle.past ), - e: e.e.replace("to do", "to become"), - f: e.f.replace(/kaw[u|ú]l/, "kedul"), - c: "v. intrans. dyn. comp.", - }); - const intransitiveFormEntry = (transitivity === "transitive" && auxVerb.entry.p === "کول") - ? makeIntransitiveFormOfEntry(entry) - : null; - return { - entry: { - // TODO: cleanup the type safety with the DictionaryNoFVars messing things up etc - entry: entry as unknown as T.VerbDictionaryEntry, - complement: comp, - }, - type: "dynamic compound", - transitivity, - yulEnding, - stem, - root, - participle, - objComplement, - auxVerb: auxVerb.entry, - ...auxVerb.complement ? { - auxVerbComplement: auxVerb.complement, - } : {}, - ...objComplement.plural ? { - singularForm: getDynamicCompoundInfo(entry, comp, true), - } : {}, - ...intransitiveFormEntry ? { - intransitiveForm: getDynamicCompoundInfo(intransitiveFormEntry, comp), - } : {}, - }; + objComplement.person + ), + }, + }; + const perfectiveStem = concatPsString(compUsed, " ", bases.stem.perfective); + const stem = { + imperfective: concatPsString(compUsed, " ", bases.stem.imperfective), + perfective: perfectiveStem, + perfectiveSplit: splitPerfective(perfectiveStem, 0, 0, true), + }; + const perfectiveRoot = concatPsString( + compUsed, + " ", + bases.root.perfective + ) as T.OptionalPersonInflections>; + const root = { + imperfective: concatPsString( + compUsed, + " ", + bases.root.imperfective + ) as T.OptionalPersonInflections>, + perfective: perfectiveRoot, + perfectiveSplit: splitPerfective(perfectiveRoot, 0, 0, true), + }; + const participle = { + present: concatPsString(compUsed, " ", auxVerb.info.participle.present), + past: concatPsString(compUsed, " ", bases.participle.past), + }; + return { + entry: { + // TODO: cleanup the type safety with the DictionaryNoFVars messing things up etc + entry: entry as unknown as T.VerbDictionaryEntry, + complement: comp, + }, + type: "generative stative compound", + transitivity, + yulEnding, + stem, + root, + participle, + objComplement, + ...(objComplement.plural + ? { + singularForm: getGenerativeStativeCompoundVerbInfo(entry, comp, true), + } + : {}), + }; } -function getObjectMatchingBases(auxInfo: T.NonComboVerbInfo, person: T.Person): Bases { - const key = getPersonInflectionsKey(person); - const getBase = (x: T.FullForm): T.SingleOrLengthOpts => ( - "mascSing" in x ? x[key] : x - ); - return { - stem: { - imperfective: getBase(auxInfo.stem.imperfective), - perfective: getBase(auxInfo.stem.perfective), - ...auxInfo.stem.perfectiveSplit ? { - perfectiveSplit: choosePersInf(auxInfo.stem.perfectiveSplit, key), - } : {}, - }, - root: { - imperfective: getBase(auxInfo.root.imperfective), - perfective: getBase(auxInfo.root.perfective), - ...auxInfo.root.perfectiveSplit ? { - perfectiveSplit: choosePersInf(auxInfo.root.perfectiveSplit, key), - } : {}, - }, - participle: { - present: getBase(auxInfo.participle.present), - past: getBase(auxInfo.participle.past), - }, - }; +function getDynamicCompoundInfo( + entry: T.DictionaryEntryNoFVars, + comp: T.DictionaryEntryNoFVars, + forceSingular?: true +): T.DynamicCompoundVerbInfo { + const transitivity = getTransitivity(entry); + const yulEnding = null; + const objComplement = getObjComplementInfo(entry, comp, forceSingular); + const auxVerb = getDynamicAuxVerb(entry); + const auxVerbInfo = getVerbInfo( + auxVerb.entry, + auxVerb.complement + ) as T.NonComboVerbInfo; + const compUsed = objComplement.plural + ? objComplement.plural + : objComplement.entry; + const bases: Bases = + auxVerbInfo.type === "stative compound" + ? getObjectMatchingBases(auxVerbInfo, objComplement.person) + : { + stem: { + imperfective: auxVerbInfo.stem.imperfective, + perfective: auxVerbInfo.stem.perfective, + ...(auxVerbInfo.stem.perfectiveSplit + ? { + perfectiveSplit: auxVerbInfo.stem.perfectiveSplit, + } + : {}), + }, + root: { + imperfective: auxVerbInfo.root.imperfective, + perfective: auxVerbInfo.root.perfective, + ...(auxVerbInfo.root.perfectiveSplit + ? { + perfectiveSplit: auxVerbInfo.root.perfectiveSplit, + } + : {}), + }, + participle: { + present: auxVerbInfo.participle.present, + past: chooseParticipleInflection( + inflectYey( + "mascSing" in auxVerbInfo.participle.past + ? // purely for type saftey, will not have mascSing + // in a non stative compound verb + /* istanbul ignore next */ + auxVerbInfo.participle.past.mascSing + : auxVerbInfo.participle.past + ), + objComplement.person + ), + }, + }; + const stem = { + imperfective: concatPsString(compUsed, " ", bases.stem.imperfective), + perfective: concatPsString(compUsed, " ", bases.stem.perfective), + ...(bases.stem.perfectiveSplit + ? { + perfectiveSplit: makeDynamicPerfectiveSplit( + compUsed, + bases.stem.perfectiveSplit + ), + } + : {}), + }; + const root = { + imperfective: concatPsString( + compUsed, + " ", + bases.root.imperfective + ) as T.OptionalPersonInflections>, + perfective: concatPsString( + compUsed, + " ", + bases.root.perfective + ) as T.OptionalPersonInflections>, + ...(bases.root.perfectiveSplit + ? { + perfectiveSplit: makeDynamicPerfectiveSplit( + compUsed, + bases.root.perfectiveSplit + ), + } + : {}), + }; + const participle = { + present: concatPsString(compUsed, " ", auxVerbInfo.participle.present), + past: concatPsString(compUsed, " ", bases.participle.past), + }; + const makeIntransitiveFormOfEntry = ( + e: T.DictionaryEntryNoFVars + ): T.DictionaryEntryNoFVars => ({ + ...e, + p: e.p.replace("کول", "کېدل"), + e: e.e.replace("to do", "to become"), + f: e.f.replace(/kaw[u|ú]l/, "kedul"), + c: "v. intrans. dyn. comp.", + }); + const intransitiveFormEntry = + transitivity === "transitive" && auxVerb.entry.p === "کول" + ? makeIntransitiveFormOfEntry(entry) + : null; + return { + entry: { + // TODO: cleanup the type safety with the DictionaryNoFVars messing things up etc + entry: entry as unknown as T.VerbDictionaryEntry, + complement: comp, + }, + type: "dynamic compound", + transitivity, + yulEnding, + stem, + root, + participle, + objComplement, + auxVerb: auxVerb.entry, + ...(auxVerb.complement + ? { + auxVerbComplement: auxVerb.complement, + } + : {}), + ...(objComplement.plural + ? { + singularForm: getDynamicCompoundInfo(entry, comp, true), + } + : {}), + ...(intransitiveFormEntry + ? { + intransitiveForm: getDynamicCompoundInfo(intransitiveFormEntry, comp), + } + : {}), + }; +} + +function getObjectMatchingBases( + auxInfo: T.NonComboVerbInfo, + person: T.Person +): Bases { + const key = getPersonInflectionsKey(person); + const getBase = ( + x: T.FullForm + ): T.SingleOrLengthOpts => ("mascSing" in x ? x[key] : x); + return { + stem: { + imperfective: getBase(auxInfo.stem.imperfective), + perfective: getBase(auxInfo.stem.perfective), + ...(auxInfo.stem.perfectiveSplit + ? { + perfectiveSplit: choosePersInf(auxInfo.stem.perfectiveSplit, key), + } + : {}), + }, + root: { + imperfective: getBase(auxInfo.root.imperfective), + perfective: getBase(auxInfo.root.perfective), + ...(auxInfo.root.perfectiveSplit + ? { + perfectiveSplit: choosePersInf(auxInfo.root.perfectiveSplit, key), + } + : {}), + }, + participle: { + present: getBase(auxInfo.participle.present), + past: getBase(auxInfo.participle.past), + }, + }; } function getObjComplementInfo( - entry: T.DictionaryEntryNoFVars, - complement: T.DictionaryEntryNoFVars, - forceSingular?: true + entry: T.DictionaryEntryNoFVars, + complement: T.DictionaryEntryNoFVars, + forceSingular?: true ): T.ObjComplement { - const complementInEntry = makePsString( - entry.p.split(" ")[0], - entry.f.split(" ")[0], - ); - const usesSeperatePluralForm = !forceSingular && !psStringEquals( - makePsString(complementInEntry.p, removeAccents(complementInEntry.f)), - makePsString(complement.p, removeAccents(complement.f)), + const complementInEntry = makePsString( + entry.p.split(" ")[0], + entry.f.split(" ")[0] + ); + const usesSeperatePluralForm = + !forceSingular && + !psStringEquals( + makePsString(complementInEntry.p, removeAccents(complementInEntry.f)), + makePsString(complement.p, removeAccents(complement.f)) ); + return { + entry: complement, + ...(usesSeperatePluralForm + ? { + plural: complementInEntry, + } + : {}), + person: getComplementPerson(complement, usesSeperatePluralForm), + }; +} + +export function getTransitivity( + entry: T.DictionaryEntryNoFVars +): T.Transitivity { + if (!entry.c) { + throw new Error("No part of speech info"); + } + if (entry.c.includes("gramm. trans.")) { + return "grammatically transitive"; + } + if (entry.c.includes("intrans.")) { + return "intransitive"; + } + return "transitive"; +} + +function getType( + entry: T.DictionaryEntry +): + | "simple" + | "stative compound" + | "dynamic compound" + | "dynamic or stative compound" + | "dynamic or generative stative compound" + | "generative stative compound" + | "transitive or grammatically transitive simple" { + // error will have thrown before on the getTransitivity function if missing entry.c + /* istanbul ignore if */ + if (!entry.c) { + throw new Error("No part of speech info"); + } + if (entry.c.includes(" trans./gramm. trans.")) { + return "transitive or grammatically transitive simple"; + } + if (entry.c.includes(" gen. stat. comp.")) { + return "generative stative compound"; + } + if (entry.c.includes(" stat. comp.")) { + return "stative compound"; + } + if (entry.c.includes(" dyn. comp.")) { + return "dynamic compound"; + } + if (entry.c.includes(" dyn./stat. comp.")) { + return "dynamic or stative compound"; + } + if (entry.c.includes(" gen. stat./dyn. comp.")) { + return "dynamic or generative stative compound"; + } + return "simple"; +} + +function getIdiosyncraticThirdMascSing( + entry: T.DictionaryEntryNoFVars +): T.ShortThirdPersFormSet | false { + if (entry.tppp && entry.tppf) { + const tpp = makePsString(entry.tppp, entry.tppf); + const ooRes = addOoPrefix(tpp, entry); return { - entry: complement, - ...usesSeperatePluralForm ? { - plural: complementInEntry, - } : {}, - person: getComplementPerson(complement, usesSeperatePluralForm), + imperfective: tpp, + perfective: ooRes.ps as T.PsString, }; -} - -function getTransitivity(entry: T.DictionaryEntryNoFVars): T.Transitivity { - if (!entry.c) { - throw new Error("No part of speech info"); + } + if (entry.p === "کول") { + if (entry.e.includes("to make")) { + return { + perfective: { p: "کړ", f: "kuR" }, + imperfective: { p: "کاوه", f: "kaawú" }, + }; } - if (entry.c.includes("gramm. trans.")) { - return "grammatically transitive"; + if (entry.e.includes("to do")) { + return { + perfective: { p: "وکړ", f: "óokuR" }, + imperfective: { p: "کاوه", f: "kaawú" }, + }; } - if (entry.c.includes("intrans.")) { - return "intransitive"; - } - return "transitive"; -} - -function getType(entry: T.DictionaryEntry): - "simple" | "stative compound" | "dynamic compound" | - "dynamic or stative compound" | "dynamic or generative stative compound" | - "generative stative compound" | "transitive or grammatically transitive simple" -{ - // error will have thrown before on the getTransitivity function if missing entry.c - /* istanbul ignore if */ - if (!entry.c) { - throw new Error("No part of speech info"); - } - if (entry.c.includes(" trans./gramm. trans.")) { - return "transitive or grammatically transitive simple"; - } - if (entry.c.includes(" gen. stat. comp.")) { - return "generative stative compound"; - } - if (entry.c.includes(" stat. comp.")) { - return "stative compound"; - } - if (entry.c.includes(" dyn. comp.")) { - return "dynamic compound"; - } - if (entry.c.includes(" dyn./stat. comp.")) { - return "dynamic or stative compound"; - } - if (entry.c.includes(" gen. stat./dyn. comp.")) { - return "dynamic or generative stative compound"; - } - return "simple"; -} - -function getIdiosyncraticThirdMascSing(entry: T.DictionaryEntryNoFVars): T.ShortThirdPersFormSet | false { - if (entry.tppp && entry.tppf) { - const tpp = makePsString(entry.tppp, entry.tppf); - const ooRes = addOoPrefix(tpp, entry) - return { - imperfective: tpp, - perfective: ooRes.ps as T.PsString, - }; - } - if (entry.p === "کول") { - if (entry.e.includes("to make")) { - return { - perfective: { p: "کړ", f: "kuR" }, - imperfective: { p: "کاوه", f: "kaawú" }, - }; - } - if (entry.e.includes("to do")) { - return { - perfective: { p: "وکړ", f: "óokuR" }, - imperfective: { p: "کاوه", f: "kaawú" }, - }; - } - } - return false; + } + return false; } /** * Returns the roots (imperfective and perfective) of a given verb - * + * * @param entry - the dictionary entry for the verb */ -function getVerbRoots(entry: T.DictionaryEntryNoFVars, transitivity: T.Transitivity, complement?: T.UnisexInflections): T.VerbRootSet { - // each of the roots compes with a short and long version - // with or without the ending ل - ul - const isKawulAux = entry.p === "کول"; - const shortAndLong = (root: T.PsString, perfective?: "perfective"): T.LengthOptions => { - const long = perfective ? root : accentOnNFromEnd(root, yulEndingInfinitive(root) ? 1 : 0); - const short = removeEndingL(root); - return { - long, - short, - ...(isKawulAux && perfective) ? { - mini: removeRetroflexR(short) - } : {}, - }; - }; - const infinitive = makePsString(entry.p, entry.f); - - // the imperfective root is the infinitive - // TODO: CHECK THIS!! FOR PERSON INFLECTIONS?? - const imperfective = ((): T.OptionalPersonInflections> => { - // if stative compound - if (complement && spaceInForm(entry)) { - const comp = complementInflects(complement) ? unisexInfToObjectMatrix(complement) : complement.masc[0][0]; - const t = getAuxTransitivity(transitivity); - const aux = stativeAux[t].info.root.imperfective - return concatPsString(removeAccentsFull(comp), " ", aux) as T.OptionalPersonInflections>; - } - return shortAndLong(entry); - })(); - - const { perfective, pSplit, fSplit } = ((): { - perfective: T.OptionalPersonInflections> - pSplit: number, - fSplit: number, - } => { - // if stative compound - if (complement) { - const comp = complementInflects(complement) ? unisexInfToObjectMatrix(complement) : complement.masc[0][0]; - const t = getAuxTransitivity(transitivity); - const aux = stativeAux[t].info.root.perfective - return { - pSplit: 0, - fSplit: 0, - perfective: concatPsString(comp, " ", aux) as T.OptionalPersonInflections>, - }; - } - // the perfective root is - // - the special perfective root if it exists, or - if (entry.prp && entry.prf) { - const perfective = shortAndLong(makePsString(entry.prp, entry.prf), "perfective"); - const hasOoPrefix = checkForOoPrefix(perfective.long); - return { - perfective, - pSplit: entry.separationAtP || (hasOoPrefix ? 1 : 0), - fSplit: entry.separationAtF || (hasOoPrefix ? 2 : 0), - }; - } - // - the infinitive prefixed with oo - const { ps, pSplit, fSplit } = addOoPrefix(infinitive, entry); - return { - perfective: shortAndLong(ps as T.PsString, "perfective"), - pSplit, - fSplit, - }; - })(); - - const perfectiveSplit = splitPerfective(perfective, pSplit, fSplit, !!complement); +function getVerbRoots( + entry: T.DictionaryEntryNoFVars, + transitivity: T.Transitivity, + complement?: T.UnisexInflections +): T.VerbRootSet { + // each of the roots compes with a short and long version + // with or without the ending ل - ul + const isKawulAux = entry.p === "کول"; + const shortAndLong = ( + root: T.PsString, + perfective?: "perfective" + ): T.LengthOptions => { + const long = perfective + ? root + : accentOnNFromEnd(root, yulEndingInfinitive(root) ? 1 : 0); + const short = removeEndingL(root); return { - imperfective, - perfective, - perfectiveSplit, + long, + short, + ...(isKawulAux && perfective + ? { + mini: removeRetroflexR(short), + } + : {}), }; + }; + const infinitive = makePsString(entry.p, entry.f); + + // the imperfective root is the infinitive + // TODO: CHECK THIS!! FOR PERSON INFLECTIONS?? + const imperfective = ((): T.OptionalPersonInflections< + T.LengthOptions + > => { + // if stative compound + if (complement && spaceInForm(entry)) { + const comp = complementInflects(complement) + ? unisexInfToObjectMatrix(complement) + : complement.masc[0][0]; + const t = getAuxTransitivity(transitivity); + const aux = stativeAux[t].info.root.imperfective; + return concatPsString( + removeAccentsFull(comp), + " ", + aux + ) as T.OptionalPersonInflections>; + } + return shortAndLong(entry); + })(); + + const { perfective, pSplit, fSplit } = ((): { + perfective: T.OptionalPersonInflections>; + pSplit: number; + fSplit: number; + } => { + // if stative compound + if (complement) { + const comp = complementInflects(complement) + ? unisexInfToObjectMatrix(complement) + : complement.masc[0][0]; + const t = getAuxTransitivity(transitivity); + const aux = stativeAux[t].info.root.perfective; + return { + pSplit: 0, + fSplit: 0, + perfective: concatPsString( + comp, + " ", + aux + ) as T.OptionalPersonInflections>, + }; + } + // the perfective root is + // - the special perfective root if it exists, or + if (entry.prp && entry.prf) { + const perfective = shortAndLong( + makePsString(entry.prp, entry.prf), + "perfective" + ); + const hasOoPrefix = checkForOoPrefix(perfective.long); + return { + perfective, + pSplit: entry.separationAtP || (hasOoPrefix ? 1 : 0), + fSplit: entry.separationAtF || (hasOoPrefix ? 2 : 0), + }; + } + // - the infinitive prefixed with oo + const { ps, pSplit, fSplit } = addOoPrefix(infinitive, entry); + return { + perfective: shortAndLong(ps as T.PsString, "perfective"), + pSplit, + fSplit, + }; + })(); + + const perfectiveSplit = splitPerfective( + perfective, + pSplit, + fSplit, + !!complement + ); + return { + imperfective, + perfective, + perfectiveSplit, + }; } -function removeAccentsFull(p: T.OptionalPersonInflections): T.OptionalPersonInflections { - if ("mascPlur" in p) { - return { - "mascSing": removeAccents(p.mascSing), - "mascPlur": removeAccents(p.mascPlur), - "femSing": removeAccents(p.femSing), - "femPlur": removeAccents(p.femPlur), - } +function removeAccentsFull( + p: T.OptionalPersonInflections +): T.OptionalPersonInflections { + if ("mascPlur" in p) { + return { + mascSing: removeAccents(p.mascSing), + mascPlur: removeAccents(p.mascPlur), + femSing: removeAccents(p.femSing), + femPlur: removeAccents(p.femPlur), }; - return removeAccents(p); + } + return removeAccents(p); } /** * Returns the stems (imperfective and perfective) of a given verb - * + * * @param entry - the dictionary entry for the verb */ -function getVerbStems(entry: T.DictionaryEntryNoFVars, root: T.VerbRootSet, transitivity: T.Transitivity, complement?: T.UnisexInflections): T.VerbStemSet { - function isRegEdulTransitive(): boolean { - /* istanbul ignore next */ - if ("mascSing" in root.imperfective) { - return false; - } - const lastPCharacters = root.imperfective.long.p.slice(-3); - return ( - // @ts-ignore - will always have a entry.c if we get to this point - (entry.c.includes("intrans.")) - && (lastPCharacters === "ېدل") - ); +function getVerbStems( + entry: T.DictionaryEntryNoFVars, + root: T.VerbRootSet, + transitivity: T.Transitivity, + complement?: T.UnisexInflections +): T.VerbStemSet { + function isRegEdulTransitive(): boolean { + /* istanbul ignore next */ + if ("mascSing" in root.imperfective) { + return false; } - function makeIntransImperfectiveStem() { - const long = { - // @ts-ignore - p: root.imperfective.long.p.slice(0, -2) + "ږ", - // @ts-ignore - f: root.imperfective.long.f.slice(0, -4) + "éG", - }; - if (entry.shortIntrans) { - const short = makePsString( - long.p.slice(0, -2), - long.f.slice(0, -2), - ); - return { long, short }; - } - return long; - } - - const imperfective = ((): T.FullForm => { - const auxTransitivity = getAuxTransitivity(transitivity); - if (complement && spaceInForm(root.imperfective)) { - const comp = complementInflects(complement) ? unisexInfToObjectMatrix(complement) : complement.masc[0][0]; - return concatPsString( - removeAccentsFull(comp), - " ", - stativeAux[auxTransitivity].info.stem.imperfective as T.PsString, - ); - } - // the imperfective stem is - // - the special present stem if it exists, or - if (entry.psp && entry.psf) { - return makePsString(entry.psp, entry.psf); - } - // - the eG form (and short form possibly) if regular transitive, or - if (isRegEdulTransitive()) { - return makeIntransImperfectiveStem() - } - // - the infinitive minus ل - return "mascSing" in root.imperfective - ? root.imperfective.mascSing.short - : root.imperfective.short; - })(); - - const { perfective, pSplit, fSplit } = ((): { perfective: T.FullForm, pSplit: number, fSplit: number } => { - if (complement) { - const comp = complementInflects(complement) ? unisexInfToObjectMatrix(complement) : complement.masc[0][0]; - const t = getAuxTransitivity(transitivity); - return { - perfective: concatPsString(comp, " ", stativeAux[t].info.stem.perfective), - pSplit: 0, - fSplit: 0, - }; - } - // the perfective stem is - // - the special subjunctive stem if it exists, or - if (entry.ssp && entry.ssf) { - const isKawulAux = entry.p === "کول"; - const perfective = makePsString(entry.ssp, entry.ssf); - const hasOoPrefix = checkForOoPrefix(perfective); - if (isKawulAux) { - return { - perfective: { - long: perfective, - short: removeRetroflexR(perfective), - }, - pSplit: hasOoPrefix ? 1 : 0, - fSplit: hasOoPrefix ? 2 : 0, - }; - } - return { - perfective, - pSplit: entry.separationAtP || (hasOoPrefix ? 1 : 0), - fSplit: entry.separationAtF || (hasOoPrefix ? 2 : 0), - }; - } - // - the perfective stem prefixed with oo (if possible) - const res = addOoPrefix(imperfective as T.SingleOrLengthOpts, entry); - return { - perfective: res.ps, - pSplit: res.pSplit, - fSplit: res.fSplit, - }; - })(); - - const perfectiveSplit = splitPerfective(perfective, pSplit, fSplit, !!complement); - return { - imperfective, - perfective, - ...perfectiveSplit ? { - perfectiveSplit, - } : {}, - }; -} - -function splitPerfective(perfective: T.FullForm, pSplit: number, fSplit: number, isStativeComp: boolean): T.SplitInfo | undefined { - if (!isStativeComp && pSplit === 0 && fSplit === 0) { - return undefined; - } - if ("mascSing" in perfective) { - // @ts-ignore - return { - mascSing: splitPerfective(perfective.mascSing, pSplit, fSplit, isStativeComp), - mascPlur: splitPerfective(perfective.mascPlur, pSplit, fSplit, isStativeComp), - femSing: splitPerfective(perfective.femSing, pSplit, fSplit, isStativeComp), - femPlur: splitPerfective(perfective.femPlur, pSplit, fSplit, isStativeComp), - }; - } - if ("long" in perfective) { - return { - // @ts-ignore - short: splitPerfective(perfective.short, pSplit, fSplit, isStativeComp) as [T.PsString, T.PsString], - long: splitPerfective(perfective.long, pSplit, fSplit, isStativeComp) as [T.PsString, T.PsString], - ..."mini" in perfective ? { - // @ts-ignore - mini: splitPerfective(perfective.mini, pSplit, fSplit, isStativeComp) as [T.PsString, T.PsString], - } : {}, - }; - } - if (isStativeComp) { - // if it's a stative, split whatever is before the aux verb with a trailing space - const pWords = perfective.p.split(" "); - const fWords = perfective.f.split(" "); - const before = makePsString( - pWords.slice(0, -1).join(" ") + " ", - fWords.slice(0, -1).join(" ") + " ", - ); - const after = makePsString( - pWords[pWords.length - 1], - fWords[fWords.length - 1], - ); - return [before, after]; - } - const pBeg = perfective.p.slice(0, pSplit); - const before = makePsString( - pBeg.endsWith(" ") ? pBeg.slice(0, -1) : pBeg, - perfective.f.slice(0, fSplit).replace("`", ""), + const lastPCharacters = root.imperfective.long.p.slice(-3); + return ( + // @ts-ignore - will always have a entry.c if we get to this point + entry.c.includes("intrans.") && lastPCharacters === "ېدل" ); - const beforeAccented = beginsWithDirectionalPronoun(before) - ? before - : accentOnFront(before); - const after = makePsString(perfective.p.slice(pSplit), removeAccents(removeStartingTick(perfective.f.slice(fSplit)))); - return [beforeAccented, after] as T.SplitInfo; -} - -function getParticiple(entry: T.DictionaryEntryNoFVars, stem: T.VerbStemSet, infinitive: T.PsString, transitivity: T.Transitivity, complement?: T.UnisexInflections): T.ParticipleSet { - const shortParticipleRoot = ((): T.PsString | null => { - const shortenableEndings = ["ښتل", "ستل", "وتل"]; - // special thing for اېښودل - پرېښودل - if (infinitive.p.slice(-4) === "ښودل" && infinitive.p.length > 4 && infinitive.p !== "کېښودل" && infinitive.p !== "کښېښودل") { - return makePsString( - infinitive.p.slice(0, -3), - infinitive.f.slice(0, -4), - ); - } - const isOrulShortenable = ["وړل", "راوړل", "وروړل"].includes(infinitive.p); - if (isOrulShortenable || (shortenableEndings.includes(infinitive.p.slice(-3)) && infinitive.p.slice(-4) !== "استل")) { - return makePsString( - infinitive.p.slice(0, -1), - infinitive.f.slice(0, -2), - ) - } - return null; - })(); - - const makeSepStativePart = (complement: T.UnisexInflections, tense: "present" | "past"): T.FullForm => { - const compInflects = complementInflects(complement); - const comp = compInflects ? unisexInfToObjectMatrix(complement) : complement.masc[0][0]; - const aux = stativeAux[auxTransitivity].info.participle[tense] as T.PsString; - return concatPsString( - removeAccentsFull(comp), - " ", - compInflects - ? unisexInfToObjectMatrix(inflectYey(aux) as T.UnisexInflections) - : aux, - ); + } + function makeIntransImperfectiveStem() { + const long = { + // @ts-ignore + p: root.imperfective.long.p.slice(0, -2) + "ږ", + // @ts-ignore + f: root.imperfective.long.f.slice(0, -4) + "éG", }; + if (entry.shortIntrans) { + const short = makePsString(long.p.slice(0, -2), long.f.slice(0, -2)); + return { long, short }; + } + return long; + } - const accentPastPart = (pp: T.PsString): T.PsString => ( - accentOnNFromEnd(pp, yulEndingInfinitive(infinitive) ? 2 : 1) - ); + const imperfective = ((): T.FullForm => { const auxTransitivity = getAuxTransitivity(transitivity); - const past = (entry.pprtp && entry.pprtf) - ? makePsString(entry.pprtp, entry.pprtf) - : complement - ? makeSepStativePart(complement, "past") - : shortParticipleRoot - ? { - short: accentPastPart( - concatPsString(ensureShortWurShwaShift(shortParticipleRoot), eyEndingUnaccented), - ), - long: accentPastPart( - concatPsString(infinitive, eyEndingUnaccented), - ), - } - : accentPastPart(concatPsString(infinitive, eyEndingUnaccented)); + if (complement && spaceInForm(root.imperfective)) { + const comp = complementInflects(complement) + ? unisexInfToObjectMatrix(complement) + : complement.masc[0][0]; + return concatPsString( + removeAccentsFull(comp), + " ", + stativeAux[auxTransitivity].info.stem.imperfective as T.PsString + ); + } + // the imperfective stem is + // - the special present stem if it exists, or + if (entry.psp && entry.psf) { + return makePsString(entry.psp, entry.psf); + } + // - the eG form (and short form possibly) if regular transitive, or + if (isRegEdulTransitive()) { + return makeIntransImperfectiveStem(); + } + // - the infinitive minus ل + return "mascSing" in root.imperfective + ? root.imperfective.mascSing.short + : root.imperfective.short; + })(); - // TODO: make this into a rule? - const shortImperfectiveRoot = (entry.p === "وتل") ? { p: "وتل", f: "watl" } : removeEndingL(infinitive); - const accentPresPart = (pp: T.PsString): T.PsString => ( - accentOnNFromEnd(pp, 1) + const { perfective, pSplit, fSplit } = ((): { + perfective: T.FullForm; + pSplit: number; + fSplit: number; + } => { + if (complement) { + const comp = complementInflects(complement) + ? unisexInfToObjectMatrix(complement) + : complement.masc[0][0]; + const t = getAuxTransitivity(transitivity); + return { + perfective: concatPsString( + comp, + " ", + stativeAux[t].info.stem.perfective + ), + pSplit: 0, + fSplit: 0, + }; + } + // the perfective stem is + // - the special subjunctive stem if it exists, or + if (entry.ssp && entry.ssf) { + const isKawulAux = entry.p === "کول"; + const perfective = makePsString(entry.ssp, entry.ssf); + const hasOoPrefix = checkForOoPrefix(perfective); + if (isKawulAux) { + return { + perfective: { + long: perfective, + short: removeRetroflexR(perfective), + }, + pSplit: hasOoPrefix ? 1 : 0, + fSplit: hasOoPrefix ? 2 : 0, + }; + } + return { + perfective, + pSplit: entry.separationAtP || (hasOoPrefix ? 1 : 0), + fSplit: entry.separationAtF || (hasOoPrefix ? 2 : 0), + }; + } + // - the perfective stem prefixed with oo (if possible) + const res = addOoPrefix( + imperfective as T.SingleOrLengthOpts, + entry ); - const present = (complement && spaceInForm(infinitive)) - ? makeSepStativePart(complement, "present") - : (shortParticipleRoot && (!psStringEquals(shortParticipleRoot, shortImperfectiveRoot) || (entry.p === "وتل"))) - ? { - short: accentPresPart( - concatPsString(shortParticipleRoot, presentParticipleSuffix), - ), - long: accentPresPart( - concatPsString(shortImperfectiveRoot, presentParticipleSuffix), - ), + return { + perfective: res.ps, + pSplit: res.pSplit, + fSplit: res.fSplit, + }; + })(); + + const perfectiveSplit = splitPerfective( + perfective, + pSplit, + fSplit, + !!complement + ); + return { + imperfective, + perfective, + ...(perfectiveSplit + ? { + perfectiveSplit, } - : ("short" in stem.imperfective && entry.shortIntrans && entry.p !== "اوسېدل") + : {}), + }; +} + +function splitPerfective( + perfective: T.FullForm, + pSplit: number, + fSplit: number, + isStativeComp: boolean +): T.SplitInfo | undefined { + if (!isStativeComp && pSplit === 0 && fSplit === 0) { + return undefined; + } + if ("mascSing" in perfective) { + // @ts-ignore + return { + mascSing: splitPerfective( + perfective.mascSing, + pSplit, + fSplit, + isStativeComp + ), + mascPlur: splitPerfective( + perfective.mascPlur, + pSplit, + fSplit, + isStativeComp + ), + femSing: splitPerfective( + perfective.femSing, + pSplit, + fSplit, + isStativeComp + ), + femPlur: splitPerfective( + perfective.femPlur, + pSplit, + fSplit, + isStativeComp + ), + }; + } + if ("long" in perfective) { + return { + // @ts-ignore + short: splitPerfective( + perfective.short, + pSplit, + fSplit, + isStativeComp + ) as [T.PsString, T.PsString], + long: splitPerfective(perfective.long, pSplit, fSplit, isStativeComp) as [ + T.PsString, + T.PsString + ], + ...("mini" in perfective ? { - short: accentPresPart( - concatPsString(stem.imperfective.short, presentParticipleSuffix), - ), - long: accentPresPart( - concatPsString(shortImperfectiveRoot, presentParticipleSuffix), - ), - } - : accentPresPart( + mini: splitPerfective( + // @ts-ignore + perfective.mini, + pSplit, + fSplit, + isStativeComp + ) as [T.PsString, T.PsString], + } + : {}), + }; + } + if (isStativeComp) { + // if it's a stative, split whatever is before the aux verb with a trailing space + const pWords = perfective.p.split(" "); + const fWords = perfective.f.split(" "); + const before = makePsString( + pWords.slice(0, -1).join(" ") + " ", + fWords.slice(0, -1).join(" ") + " " + ); + const after = makePsString( + pWords[pWords.length - 1], + fWords[fWords.length - 1] + ); + return [before, after]; + } + const pBeg = perfective.p.slice(0, pSplit); + const before = makePsString( + pBeg.endsWith(" ") ? pBeg.slice(0, -1) : pBeg, + perfective.f.slice(0, fSplit).replace("`", "") + ); + const beforeAccented = beginsWithDirectionalPronoun(before) + ? before + : accentOnFront(before); + const after = makePsString( + perfective.p.slice(pSplit), + removeAccents(removeStartingTick(perfective.f.slice(fSplit))) + ); + return [beforeAccented, after] as T.SplitInfo; +} + +function getParticiple( + entry: T.DictionaryEntryNoFVars, + stem: T.VerbStemSet, + infinitive: T.PsString, + transitivity: T.Transitivity, + complement?: T.UnisexInflections +): T.ParticipleSet { + const shortParticipleRoot = ((): T.PsString | null => { + const shortenableEndings = ["ښتل", "ستل", "وتل"]; + // special thing for اېښودل - پرېښودل + if ( + infinitive.p.slice(-4) === "ښودل" && + infinitive.p.length > 4 && + infinitive.p !== "کېښودل" && + infinitive.p !== "کښېښودل" + ) { + return makePsString(infinitive.p.slice(0, -3), infinitive.f.slice(0, -4)); + } + const isOrulShortenable = ["وړل", "راوړل", "وروړل"].includes(infinitive.p); + if ( + isOrulShortenable || + (shortenableEndings.includes(infinitive.p.slice(-3)) && + infinitive.p.slice(-4) !== "استل") + ) { + return makePsString(infinitive.p.slice(0, -1), infinitive.f.slice(0, -2)); + } + return null; + })(); + + const makeSepStativePart = ( + complement: T.UnisexInflections, + tense: "present" | "past" + ): T.FullForm => { + const compInflects = complementInflects(complement); + const comp = compInflects + ? unisexInfToObjectMatrix(complement) + : complement.masc[0][0]; + const aux = stativeAux[auxTransitivity].info.participle[ + tense + ] as T.PsString; + return concatPsString( + removeAccentsFull(comp), + " ", + compInflects + ? unisexInfToObjectMatrix(inflectYey(aux) as T.UnisexInflections) + : aux + ); + }; + + const accentPastPart = (pp: T.PsString): T.PsString => + accentOnNFromEnd(pp, yulEndingInfinitive(infinitive) ? 2 : 1); + const auxTransitivity = getAuxTransitivity(transitivity); + const past = + entry.pprtp && entry.pprtf + ? makePsString(entry.pprtp, entry.pprtf) + : complement + ? makeSepStativePart(complement, "past") + : shortParticipleRoot + ? { + short: accentPastPart( concatPsString( - shortImperfectiveRoot.p === "وړ" ? ensureShortWurShwaShift(shortImperfectiveRoot) : shortImperfectiveRoot, - presentParticipleSuffix, - ), + ensureShortWurShwaShift(shortParticipleRoot), + eyEndingUnaccented + ) + ), + long: accentPastPart(concatPsString(infinitive, eyEndingUnaccented)), + } + : accentPastPart(concatPsString(infinitive, eyEndingUnaccented)); + + // TODO: make this into a rule? + const shortImperfectiveRoot = + entry.p === "وتل" ? { p: "وتل", f: "watl" } : removeEndingL(infinitive); + const accentPresPart = (pp: T.PsString): T.PsString => + accentOnNFromEnd(pp, 1); + const present = + complement && spaceInForm(infinitive) + ? makeSepStativePart(complement, "present") + : shortParticipleRoot && + (!psStringEquals(shortParticipleRoot, shortImperfectiveRoot) || + entry.p === "وتل") + ? { + short: accentPresPart( + concatPsString(shortParticipleRoot, presentParticipleSuffix) + ), + long: accentPresPart( + concatPsString(shortImperfectiveRoot, presentParticipleSuffix) + ), + } + : "short" in stem.imperfective && + entry.shortIntrans && + entry.p !== "اوسېدل" + ? { + short: accentPresPart( + concatPsString(stem.imperfective.short, presentParticipleSuffix) + ), + long: accentPresPart( + concatPsString(shortImperfectiveRoot, presentParticipleSuffix) + ), + } + : accentPresPart( + concatPsString( + shortImperfectiveRoot.p === "وړ" + ? ensureShortWurShwaShift(shortImperfectiveRoot) + : shortImperfectiveRoot, + presentParticipleSuffix + ) ); - return { - past, - present, - }; + return { + past, + present, + }; } /** * Adds a perfective و - oo prefix to a verb - * + * * @param entry - the dictionary entry for the verb */ function addOoPrefix( - s: T.SingleOrLengthOpts, - entry: T.DictionaryEntryNoFVars, -): { ps: T.SingleOrLengthOpts, pSplit: number, fSplit: number } { - let pSplit = 0; - let fSplit = 0; - // A bit of side effects in this function... sorry! - function attachOo(ps: T.PsString): T.PsString; - function attachOo(ps: T.SingleOrLengthOpts): T.SingleOrLengthOpts; - function attachOo(ps: T.SingleOrLengthOpts): T.SingleOrLengthOpts { - if ("long" in ps) { - return { - short: attachOo(ps.short), - long: attachOo(ps.long), - }; - } - if (entry.separationAtP && entry.separationAtF) { - pSplit = entry.separationAtP; - fSplit = entry.separationAtF; - return ps; - } - if (entry.noOo) { - return ps; - } - if (entry.sepOo) { - pSplit = 2; - fSplit = 3; - return { - p: `و ${ps.p}`, - f: `oo\`${ps.f}`, - }; - } - const startsWithA = ps.p.charAt(0) === "ا" && ps.f.charAt(0) === "a"; - if (startsWithA) { - pSplit = 2; - fSplit = 3; - return { - p: `و${ps.p}`, - f: `wa${ps.f}`, - }; - } - const startsWithAa = ["آ", "ا"].includes(ps.p.charAt(0)) && ps.f.slice(0, 2) === "aa"; - if (startsWithAa) { - pSplit = 2; - fSplit = 3; - return { - p: `وا${ps.p.substr(1)}`, - f: `w${ps.f}`, - }; - } - const startsWithOo = ["óo", "oo"].includes(ps.f.slice(0, 2)); - if (startsWithOo) { - pSplit = 1; - fSplit = 2; - return { - p: `و${ps.p}`, - f: `wU${ps.f}`, - }; - } - const startsWithEe = ["ée", "ee"].includes(ps.f.slice(0, 2)) && ps.p.slice(0, 2) === "ای"; - const startsWithE = ["e", "é"].includes(ps.f[0]) && ps.p.slice(0, 2) === "اې"; - if (startsWithEe || startsWithE) { - pSplit = 2; - fSplit = startsWithEe ? 3 : 2; - return { - p: `و${ps.p.slice(1)}`, - f: `w${ps.f}`, - }; - } - const startsWithO = ["ó", "o"].includes(ps.f[0]) && ps.p.slice(0, 2) === "او"; - if (startsWithO) { - pSplit = 1; - fSplit = 2; - return { - p: `و${ps.p}`, - f: `oo\`${ps.f}`, - }; - } - pSplit = 1; - fSplit = 2; - return { - p: `و${ps.p}`, - f: `oo${ps.f}`, - }; + s: T.SingleOrLengthOpts, + entry: T.DictionaryEntryNoFVars +): { ps: T.SingleOrLengthOpts; pSplit: number; fSplit: number } { + let pSplit = 0; + let fSplit = 0; + // A bit of side effects in this function... sorry! + function attachOo(ps: T.PsString): T.PsString; + function attachOo( + ps: T.SingleOrLengthOpts + ): T.SingleOrLengthOpts; + function attachOo( + ps: T.SingleOrLengthOpts + ): T.SingleOrLengthOpts { + if ("long" in ps) { + return { + short: attachOo(ps.short), + long: attachOo(ps.long), + }; } - const attachedOo = attachOo(s); + if (entry.separationAtP && entry.separationAtF) { + pSplit = entry.separationAtP; + fSplit = entry.separationAtF; + return ps; + } + if (entry.noOo) { + return ps; + } + if (entry.sepOo) { + pSplit = 2; + fSplit = 3; + return { + p: `و ${ps.p}`, + f: `oo\`${ps.f}`, + }; + } + const startsWithA = ps.p.charAt(0) === "ا" && ps.f.charAt(0) === "a"; + if (startsWithA) { + pSplit = 2; + fSplit = 3; + return { + p: `و${ps.p}`, + f: `wa${ps.f}`, + }; + } + const startsWithAa = + ["آ", "ا"].includes(ps.p.charAt(0)) && ps.f.slice(0, 2) === "aa"; + if (startsWithAa) { + pSplit = 2; + fSplit = 3; + return { + p: `وا${ps.p.substr(1)}`, + f: `w${ps.f}`, + }; + } + const startsWithOo = ["óo", "oo"].includes(ps.f.slice(0, 2)); + if (startsWithOo) { + pSplit = 1; + fSplit = 2; + return { + p: `و${ps.p}`, + f: `wU${ps.f}`, + }; + } + const startsWithEe = + ["ée", "ee"].includes(ps.f.slice(0, 2)) && ps.p.slice(0, 2) === "ای"; + const startsWithE = + ["e", "é"].includes(ps.f[0]) && ps.p.slice(0, 2) === "اې"; + if (startsWithEe || startsWithE) { + pSplit = 2; + fSplit = startsWithEe ? 3 : 2; + return { + p: `و${ps.p.slice(1)}`, + f: `w${ps.f}`, + }; + } + const startsWithO = + ["ó", "o"].includes(ps.f[0]) && ps.p.slice(0, 2) === "او"; + if (startsWithO) { + pSplit = 1; + fSplit = 2; + return { + p: `و${ps.p}`, + f: `oo\`${ps.f}`, + }; + } + pSplit = 1; + fSplit = 2; return { - ps: accentOnFront(attachedOo), - pSplit: entry.separationAtP ? entry.separationAtP : pSplit, - fSplit: entry.separationAtF ? entry.separationAtF : fSplit, + p: `و${ps.p}`, + f: `oo${ps.f}`, }; + } + const attachedOo = attachOo(s); + return { + ps: accentOnFront(attachedOo), + pSplit: entry.separationAtP ? entry.separationAtP : pSplit, + fSplit: entry.separationAtF ? entry.separationAtF : fSplit, + }; } -function ensureUnisexInf(complement: T.DictionaryEntryNoFVars): T.UnisexInflections { - const inf = inflectWord(complement); - if (inf !== false && !!inf.inflections && isUnisexSet(inf.inflections)) { - return inf.inflections as T.UnisexInflections; - } - const word = makePsString(complement.p, complement.f); - return { - masc: [ - [word], - [word], - [word], - ], - fem: [ - [word], - [word], - [word], - ], - }; +function ensureUnisexInf( + complement: T.DictionaryEntryNoFVars +): T.UnisexInflections { + const inf = inflectWord(complement); + if (inf !== false && !!inf.inflections && isUnisexSet(inf.inflections)) { + return inf.inflections as T.UnisexInflections; + } + const word = makePsString(complement.p, complement.f); + return { + masc: [[word], [word], [word]], + fem: [[word], [word], [word]], + }; } function getDynamicAuxVerb(entry: T.DictionaryEntryNoFVars): { - entry: T.DictionaryEntryNoFVars, - complement?: T.DictionaryEntryNoFVars, + entry: T.DictionaryEntryNoFVars; + complement?: T.DictionaryEntryNoFVars; } { - const auxWord = entry.p.trim().split(" ").slice(-1)[0]; - const auxWordResult = dynamicAuxVerbs.find((a) => a.entry.p === auxWord); - /* istanbul ignore next */ - if (!auxWordResult) { - throw new Error("unknown auxilary verb for dynamic compound"); - } - return { - entry: removeFVarients(auxWordResult.entry), - ...("complement" in auxWordResult) ? { - complement: auxWordResult.complement ? removeFVarients(auxWordResult.complement) : undefined, - } : {}, - }; + const auxWord = entry.p.trim().split(" ").slice(-1)[0]; + const auxWordResult = dynamicAuxVerbs.find((a) => a.entry.p === auxWord); + /* istanbul ignore next */ + if (!auxWordResult) { + throw new Error("unknown auxilary verb for dynamic compound"); + } + return { + entry: removeFVarients(auxWordResult.entry), + ...("complement" in auxWordResult + ? { + complement: auxWordResult.complement + ? removeFVarients(auxWordResult.complement) + : undefined, + } + : {}), + }; } function getComplementPerson( - complement: T.DictionaryEntryNoFVars, - usesSeperatePluralForm?: boolean, + complement: T.DictionaryEntryNoFVars, + usesSeperatePluralForm?: boolean ): T.Person { - const number = ( - (complement.c && complement.c.includes("pl.")) || usesSeperatePluralForm - ) ? "plural" : "singular"; - const gender = (complement.c && complement.c.includes("n. m.")) ? "masc" : "fem"; - return getPersonNumber(gender, number); + const number = + (complement.c && complement.c.includes("pl.")) || usesSeperatePluralForm + ? "plural" + : "singular"; + const gender = + complement.c && complement.c.includes("n. m.") ? "masc" : "fem"; + return getPersonNumber(gender, number); } -function makeDynamicPerfectiveSplit(comp: T.PsString, auxSplit: T.SplitInfo): T.SplitInfo { - if ("mascSing" in auxSplit) { - return { - mascSing: makeDynamicPerfectiveSplit(comp, auxSplit.mascSing) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>, - mascPlur: makeDynamicPerfectiveSplit(comp, auxSplit.mascPlur) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>, - femSing: makeDynamicPerfectiveSplit(comp, auxSplit.femSing) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>, - femPlur: makeDynamicPerfectiveSplit(comp, auxSplit.femPlur) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>, - }; - } - if ("long" in auxSplit) { - return { - long: makeDynamicPerfectiveSplit(comp, auxSplit.long) as [T.PsString, T.PsString], - short: makeDynamicPerfectiveSplit(comp, auxSplit.short) as [T.PsString, T.PsString], - ...auxSplit.mini ? { - mini: makeDynamicPerfectiveSplit(comp, auxSplit.mini) as [T.PsString, T.PsString], - } : {}, - }; - } - return [ - concatPsString(comp, " ", auxSplit[0]), - auxSplit[1], - ]; -} - -export function getAbilityRootsAndStems(info: T.NonComboVerbInfo): T.AbilityRootsAndStems { - const isIntransitiveStativeCompound = - (info.type === "stative compound" && info.transitivity === "intransitive") - || isTlulVerb(info.entry); - const roots = getAbilityRoots(info.root, isIntransitiveStativeCompound); - return addAbilityHelperRootsAndStems(roots, isIntransitiveStativeCompound); -} - -function addAbilityHelperRootsAndStems(roots: T.VerbRootSet, isIntransitiveStativeCompound: boolean): T.AbilityRootsAndStems { - function addAbilityHelperToRoot( - r: T.OptionalPersonInflections>, - helper: T.PsString, - ): T.OptionalPersonInflections> { - if ("mascSing" in r) { - return { - mascSing: addAbilityHelperToRoot(r.mascSing, helper) as T.LengthOptions, - mascPlur: addAbilityHelperToRoot(r.mascPlur, helper) as T.LengthOptions, - femSing: addAbilityHelperToRoot(r.femSing, helper) as T.LengthOptions, - femPlur: addAbilityHelperToRoot(r.femPlur, helper) as T.LengthOptions, - } - } - return { - long: concatPsString(r.long, " ", helper), - short: concatPsString(r.short, " ", helper), - }; - } - const stemHelper = getLong(noPersInfs(kedulStat.info.stem.perfective)); - const rootHelper = noPersInfs(kedulStat.info.root.perfective).long; +function makeDynamicPerfectiveSplit( + comp: T.PsString, + auxSplit: T.SplitInfo +): T.SplitInfo { + if ("mascSing" in auxSplit) { return { - stem: { - perfective: addAbilityHelperToRoot(roots.perfective, stemHelper), - imperfective: addAbilityHelperToRoot(roots.imperfective, stemHelper), - ...roots.perfectiveSplit ? { - perfectiveSplit: addAbilityHelperToPerfectiveSplit(roots.perfectiveSplit, stemHelper), - } : {}, - }, - root: { - perfective: addAbilityHelperToRoot(roots.perfective, rootHelper), - imperfective: addAbilityHelperToRoot(roots.imperfective, rootHelper), - ...roots.perfectiveSplit ? { - perfectiveSplit: addAbilityHelperToPerfectiveSplit(roots.perfectiveSplit, rootHelper), - } : {}, - }, + mascSing: makeDynamicPerfectiveSplit( + comp, + auxSplit.mascSing + ) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>, + mascPlur: makeDynamicPerfectiveSplit( + comp, + auxSplit.mascPlur + ) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>, + femSing: makeDynamicPerfectiveSplit( + comp, + auxSplit.femSing + ) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>, + femPlur: makeDynamicPerfectiveSplit( + comp, + auxSplit.femPlur + ) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>, }; + } + if ("long" in auxSplit) { + return { + long: makeDynamicPerfectiveSplit(comp, auxSplit.long) as [ + T.PsString, + T.PsString + ], + short: makeDynamicPerfectiveSplit(comp, auxSplit.short) as [ + T.PsString, + T.PsString + ], + ...(auxSplit.mini + ? { + mini: makeDynamicPerfectiveSplit(comp, auxSplit.mini) as [ + T.PsString, + T.PsString + ], + } + : {}), + }; + } + return [concatPsString(comp, " ", auxSplit[0]), auxSplit[1]]; } -function addAbilityHelperToPerfectiveSplit(s: T.SplitInfo, helper: T.PsString): T.SplitInfo { - if ("mascSing" in s) { - return { - mascSing: addAbilityHelperToPerfectiveSplit(s.mascSing, helper) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>, - mascPlur: addAbilityHelperToPerfectiveSplit(s.mascPlur, helper) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>, - femSing: addAbilityHelperToPerfectiveSplit(s.femSing, helper) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>, - femPlur: addAbilityHelperToPerfectiveSplit(s.femPlur, helper) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>, - }; +export function getAbilityRootsAndStems( + info: T.NonComboVerbInfo +): T.AbilityRootsAndStems { + const isIntransitiveStativeCompound = + (info.type === "stative compound" && + info.transitivity === "intransitive") || + isTlulVerb(info.entry); + const roots = getAbilityRoots(info.root, isIntransitiveStativeCompound); + return addAbilityHelperRootsAndStems(roots, isIntransitiveStativeCompound); +} + +function addAbilityHelperRootsAndStems( + roots: T.VerbRootSet, + isIntransitiveStativeCompound: boolean +): T.AbilityRootsAndStems { + function addAbilityHelperToRoot( + r: T.OptionalPersonInflections>, + helper: T.PsString + ): T.OptionalPersonInflections> { + if ("mascSing" in r) { + return { + mascSing: addAbilityHelperToRoot( + r.mascSing, + helper + ) as T.LengthOptions, + mascPlur: addAbilityHelperToRoot( + r.mascPlur, + helper + ) as T.LengthOptions, + femSing: addAbilityHelperToRoot( + r.femSing, + helper + ) as T.LengthOptions, + femPlur: addAbilityHelperToRoot( + r.femPlur, + helper + ) as T.LengthOptions, + }; } return { - long: [ - getLong(s)[0], - concatPsString(getLong(s)[1], " ", helper), - ], - short: [ - getShort(s)[0], - concatPsString(getShort(s)[1], " ", helper), - ], + long: concatPsString(r.long, " ", helper), + short: concatPsString(r.short, " ", helper), }; + } + const stemHelper = getLong(noPersInfs(kedulStat.info.stem.perfective)); + const rootHelper = noPersInfs(kedulStat.info.root.perfective).long; + return { + stem: { + perfective: addAbilityHelperToRoot(roots.perfective, stemHelper), + imperfective: addAbilityHelperToRoot(roots.imperfective, stemHelper), + ...(roots.perfectiveSplit + ? { + perfectiveSplit: addAbilityHelperToPerfectiveSplit( + roots.perfectiveSplit, + stemHelper + ), + } + : {}), + }, + root: { + perfective: addAbilityHelperToRoot(roots.perfective, rootHelper), + imperfective: addAbilityHelperToRoot(roots.imperfective, rootHelper), + ...(roots.perfectiveSplit + ? { + perfectiveSplit: addAbilityHelperToPerfectiveSplit( + roots.perfectiveSplit, + rootHelper + ), + } + : {}), + }, + }; } -export function getPassiveRootsAndStems(info: T.NonComboVerbInfo, withTails?: boolean): T.PassiveRootsAndStems | undefined { - if (info.transitivity === "intransitive") return undefined; +function addAbilityHelperToPerfectiveSplit( + s: T.SplitInfo, + helper: T.PsString +): T.SplitInfo { + if ("mascSing" in s) { return { - stem: getPassiveStem(info.root, info.root.perfectiveSplit, withTails), - root: getPassiveRoot(info.root, info.root.perfectiveSplit, withTails), - participle: { - past: getPassivePastParticiple(info.root.imperfective, withTails), - }, + mascSing: addAbilityHelperToPerfectiveSplit( + s.mascSing, + helper + ) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>, + mascPlur: addAbilityHelperToPerfectiveSplit( + s.mascPlur, + helper + ) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>, + femSing: addAbilityHelperToPerfectiveSplit( + s.femSing, + helper + ) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>, + femPlur: addAbilityHelperToPerfectiveSplit( + s.femPlur, + helper + ) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>, }; + } + return { + long: [getLong(s)[0], concatPsString(getLong(s)[1], " ", helper)], + short: [getShort(s)[0], concatPsString(getShort(s)[1], " ", helper)], + }; +} + +export function getPassiveRootsAndStems( + info: T.NonComboVerbInfo, + withTails?: boolean +): T.PassiveRootsAndStems | undefined { + if (info.transitivity === "intransitive") return undefined; + return { + stem: getPassiveStem(info.root, info.root.perfectiveSplit, withTails), + root: getPassiveRoot(info.root, info.root.perfectiveSplit, withTails), + participle: { + past: getPassivePastParticiple(info.root.imperfective, withTails), + }, + }; } const passiveRootTail: T.PsString = { p: "ی", f: "ey" }; -function getPassiveStem(root: T.VerbRootSet, splitInfo: T.SplitInfo | undefined, withTails?: boolean): T.VerbStemSet { - const perfectiveRoot = withTails ? concatPsString(root.perfective, passiveRootTail) : root.perfective; - const imperfectiveRoot = withTails ? concatPsString(root.imperfective, passiveRootTail) : root.imperfective; - return { - perfective: getPassiveStemAspect(perfectiveRoot, "perfective"), - imperfective: getPassiveStemAspect(imperfectiveRoot, "imperfective"), - ...splitInfo ? { - perfectiveSplit: getPassiveStemPerfectiveSplit(perfectiveRoot, splitInfo), - } : {}, - }; -} - -function getPassiveStemPerfectiveSplit(stem: T.OptionalPersonInflections>, splitInfo: T.SplitInfo): T.SplitInfo { - const si = "long" in splitInfo ? splitInfo.long : splitInfo; - if ("mascSing" in si) { - if (!("mascSing" in stem)) throw new Error("persInflections doesn't match perfective split"); - return { - "mascSing": getPassiveStemPerfectiveSplit(stem.mascSing, si.mascSing) as [T.PsString, T.PsString], - "mascPlur": getPassiveStemPerfectiveSplit(stem.mascPlur, si.mascPlur) as [T.PsString, T.PsString], - "femSing": getPassiveStemPerfectiveSplit(stem.femSing, si.femSing) as [T.PsString, T.PsString], - "femPlur": getPassiveStemPerfectiveSplit(stem.femPlur, si.femPlur) as [T.PsString, T.PsString], +function getPassiveStem( + root: T.VerbRootSet, + splitInfo: T.SplitInfo | undefined, + withTails?: boolean +): T.VerbStemSet { + const perfectiveRoot = withTails + ? concatPsString(root.perfective, passiveRootTail) + : root.perfective; + const imperfectiveRoot = withTails + ? concatPsString(root.imperfective, passiveRootTail) + : root.imperfective; + return { + perfective: getPassiveStemAspect(perfectiveRoot, "perfective"), + imperfective: getPassiveStemAspect(imperfectiveRoot, "imperfective"), + ...(splitInfo + ? { + perfectiveSplit: getPassiveStemPerfectiveSplit( + perfectiveRoot, + splitInfo + ), } - } - return [ - si[0], - // @ts-ignore - concatPsString(si[1], " ", stativeAux.intransitive.info.stem.perfective), - ]; + : {}), + }; } -function getPassiveRootPerfectiveSplit(root: T.OptionalPersonInflections>, splitInfo: T.SplitInfo): T.SplitInfo { - const si = "long" in splitInfo ? splitInfo.long : splitInfo; - if ("mascSing" in si) { - if (!("mascSing" in root)) throw new Error("persInflections doesn't match perfective split"); - return { - "mascSing": getPassiveRootPerfectiveSplit(root.mascSing, si.mascSing) as [T.PsString, T.PsString], - "mascPlur": getPassiveRootPerfectiveSplit(root.mascPlur, si.mascPlur) as [T.PsString, T.PsString], - "femSing": getPassiveRootPerfectiveSplit(root.femSing, si.femSing) as [T.PsString, T.PsString], - "femPlur": getPassiveRootPerfectiveSplit(root.femPlur, si.femPlur) as [T.PsString, T.PsString], - }; - } +function getPassiveStemPerfectiveSplit( + stem: T.OptionalPersonInflections>, + splitInfo: T.SplitInfo +): T.SplitInfo { + const si = "long" in splitInfo ? splitInfo.long : splitInfo; + if ("mascSing" in si) { + if (!("mascSing" in stem)) + throw new Error("persInflections doesn't match perfective split"); return { - short: [ - si[0], - // @ts-ignore - concatPsString(si[1], " ", stativeAux.intransitive.info.root.perfective.short), - ], - long: [ - si[0], - // @ts-ignore - concatPsString(si[1], " ", stativeAux.intransitive.info.root.perfective.long), - ], + mascSing: getPassiveStemPerfectiveSplit(stem.mascSing, si.mascSing) as [ + T.PsString, + T.PsString + ], + mascPlur: getPassiveStemPerfectiveSplit(stem.mascPlur, si.mascPlur) as [ + T.PsString, + T.PsString + ], + femSing: getPassiveStemPerfectiveSplit(stem.femSing, si.femSing) as [ + T.PsString, + T.PsString + ], + femPlur: getPassiveStemPerfectiveSplit(stem.femPlur, si.femPlur) as [ + T.PsString, + T.PsString + ], }; + } + return [ + si[0], + // @ts-ignore + concatPsString(si[1], " ", stativeAux.intransitive.info.stem.perfective), + ]; +} + +function getPassiveRootPerfectiveSplit( + root: T.OptionalPersonInflections>, + splitInfo: T.SplitInfo +): T.SplitInfo { + const si = "long" in splitInfo ? splitInfo.long : splitInfo; + if ("mascSing" in si) { + if (!("mascSing" in root)) + throw new Error("persInflections doesn't match perfective split"); + return { + mascSing: getPassiveRootPerfectiveSplit(root.mascSing, si.mascSing) as [ + T.PsString, + T.PsString + ], + mascPlur: getPassiveRootPerfectiveSplit(root.mascPlur, si.mascPlur) as [ + T.PsString, + T.PsString + ], + femSing: getPassiveRootPerfectiveSplit(root.femSing, si.femSing) as [ + T.PsString, + T.PsString + ], + femPlur: getPassiveRootPerfectiveSplit(root.femPlur, si.femPlur) as [ + T.PsString, + T.PsString + ], + }; + } + return { + short: [ + si[0], + concatPsString( + si[1], + " ", + // @ts-ignore + stativeAux.intransitive.info.root.perfective.short + ), + ], + long: [ + si[0], + concatPsString( + si[1], + " ", + // @ts-ignore + stativeAux.intransitive.info.root.perfective.long + ), + ], + }; } const abilityTail = { p: "ی", f: "ey" }; const abilityTailAccented = { p: "ی", f: "éy" }; -function getAbilityRoots(root: T.VerbRootSet, isIntransitiveStativeCompound: boolean): T.VerbRootSet { - function getAspectAbilityRoot(root: T.VerbRootSet[T.Aspect], aspect: T.Aspect): T.OptionalPersonInflections> { - if ("mascSing" in root) { - return { - mascSing: getAspectAbilityRoot(root.mascSing, aspect) as T.LengthOptions, - mascPlur: getAspectAbilityRoot(root.mascPlur, aspect) as T.LengthOptions, - femSing: getAspectAbilityRoot(root.femSing, aspect) as T.LengthOptions, - femPlur: getAspectAbilityRoot(root.femPlur, aspect) as T.LengthOptions, - }; - } - return { - long: concatPsString(root.long, abilityTail) as T.PsString, - short: concatPsString(root.short, aspect === "imperfective" ? abilityTailAccented : abilityTail) as T.PsString, - } - } - function getAbilityRootPerfectiveSplit(s: T.SplitInfo): T.SplitInfo { - if ("mascSing" in s) { - return { - mascSing: getAbilityRootPerfectiveSplit(s.mascSing) as [T.PsString, T.PsString], - mascPlur: getAbilityRootPerfectiveSplit(s.mascPlur) as [T.PsString, T.PsString], - femSing: getAbilityRootPerfectiveSplit(s.femSing) as [T.PsString, T.PsString], - femPlur: getAbilityRootPerfectiveSplit(s.femPlur) as [T.PsString, T.PsString], - }; - } - return { - long: [getLong(s)[0], concatPsString(getLong(s)[1], abilityTail)], - short: [getShort(s)[0], concatPsString(getShort(s)[1], abilityTail)], - } - } - return { - perfective: getAspectAbilityRoot( - !isIntransitiveStativeCompound ? root.perfective : root.imperfective, - !isIntransitiveStativeCompound ? "perfective" : "imperfective", - ), - imperfective: getAspectAbilityRoot(root.imperfective, "imperfective"), - ...(root.perfectiveSplit && !isIntransitiveStativeCompound) ? { - perfectiveSplit: getAbilityRootPerfectiveSplit(root.perfectiveSplit), - } : {}, - }; -} - -function getPassiveRoot(root: T.VerbRootSet, splitInfo: T.SplitInfo | undefined, withTails?: boolean): T.VerbRootSet { - const perfectiveRoot = withTails ? concatPsString(root.perfective, passiveRootTail) : root.perfective; - const imperfectiveRoot = withTails ? concatPsString(root.imperfective, passiveRootTail) : root.imperfective; - return { - perfective: getPassiveRootAspect(perfectiveRoot, "perfective"), - imperfective: getPassiveRootAspect(imperfectiveRoot, "imperfective"), - ...splitInfo ? { - perfectiveSplit: getPassiveRootPerfectiveSplit(perfectiveRoot, splitInfo), - } : {}, - }; -} - -function getPassivePastParticiple(root: T.OptionalPersonInflections>, withTails?: boolean): T.OptionalPersonInflections> { +function getAbilityRoots( + root: T.VerbRootSet, + isIntransitiveStativeCompound: boolean +): T.VerbRootSet { + function getAspectAbilityRoot( + root: T.VerbRootSet[T.Aspect], + aspect: T.Aspect + ): T.OptionalPersonInflections> { if ("mascSing" in root) { - return { - "mascSing": getPassivePastParticiple(root.mascSing) as T.LengthOptions, - "mascPlur": getPassivePastParticiple(root.mascPlur) as T.LengthOptions, - "femSing": getPassivePastParticiple(root.femPlur) as T.LengthOptions, - "femPlur": getPassivePastParticiple(root.femPlur) as T.LengthOptions, - }; + return { + mascSing: getAspectAbilityRoot( + root.mascSing, + aspect + ) as T.LengthOptions, + mascPlur: getAspectAbilityRoot( + root.mascPlur, + aspect + ) as T.LengthOptions, + femSing: getAspectAbilityRoot( + root.femSing, + aspect + ) as T.LengthOptions, + femPlur: getAspectAbilityRoot( + root.femPlur, + aspect + ) as T.LengthOptions, + }; } - const r = withTails ? concatPsString(root, passiveRootTail) : root; + return { + long: concatPsString(root.long, abilityTail) as T.PsString, + short: concatPsString( + root.short, + aspect === "imperfective" ? abilityTailAccented : abilityTail + ) as T.PsString, + }; + } + function getAbilityRootPerfectiveSplit(s: T.SplitInfo): T.SplitInfo { + if ("mascSing" in s) { + return { + mascSing: getAbilityRootPerfectiveSplit(s.mascSing) as [ + T.PsString, + T.PsString + ], + mascPlur: getAbilityRootPerfectiveSplit(s.mascPlur) as [ + T.PsString, + T.PsString + ], + femSing: getAbilityRootPerfectiveSplit(s.femSing) as [ + T.PsString, + T.PsString + ], + femPlur: getAbilityRootPerfectiveSplit(s.femPlur) as [ + T.PsString, + T.PsString + ], + }; + } + return { + long: [getLong(s)[0], concatPsString(getLong(s)[1], abilityTail)], + short: [getShort(s)[0], concatPsString(getShort(s)[1], abilityTail)], + }; + } + return { + perfective: getAspectAbilityRoot( + !isIntransitiveStativeCompound ? root.perfective : root.imperfective, + !isIntransitiveStativeCompound ? "perfective" : "imperfective" + ), + imperfective: getAspectAbilityRoot(root.imperfective, "imperfective"), + ...(root.perfectiveSplit && !isIntransitiveStativeCompound + ? { + perfectiveSplit: getAbilityRootPerfectiveSplit(root.perfectiveSplit), + } + : {}), + }; +} + +function getPassiveRoot( + root: T.VerbRootSet, + splitInfo: T.SplitInfo | undefined, + withTails?: boolean +): T.VerbRootSet { + const perfectiveRoot = withTails + ? concatPsString(root.perfective, passiveRootTail) + : root.perfective; + const imperfectiveRoot = withTails + ? concatPsString(root.imperfective, passiveRootTail) + : root.imperfective; + return { + perfective: getPassiveRootAspect(perfectiveRoot, "perfective"), + imperfective: getPassiveRootAspect(imperfectiveRoot, "imperfective"), + ...(splitInfo + ? { + perfectiveSplit: getPassiveRootPerfectiveSplit( + perfectiveRoot, + splitInfo + ), + } + : {}), + }; +} + +function getPassivePastParticiple( + root: T.OptionalPersonInflections>, + withTails?: boolean +): T.OptionalPersonInflections> { + if ("mascSing" in root) { + return { + mascSing: getPassivePastParticiple( + root.mascSing + ) as T.LengthOptions, + mascPlur: getPassivePastParticiple( + root.mascPlur + ) as T.LengthOptions, + femSing: getPassivePastParticiple( + root.femPlur + ) as T.LengthOptions, + femPlur: getPassivePastParticiple( + root.femPlur + ) as T.LengthOptions, + }; + } + const r = withTails ? concatPsString(root, passiveRootTail) : root; + // @ts-ignore + return concatPsString( + removeAccents(getLong(r)), + " ", + stativeAux.intransitive.info.participle.past + ) as T.PsString; +} + +function getPassiveStemAspect( + root: T.FullForm, + aspect: T.Aspect +): T.FullForm { + if ("mascSing" in root) { + return { + mascSing: getPassiveStemAspect(root.mascSing, aspect) as T.PsString, + mascPlur: getPassiveStemAspect(root.mascPlur, aspect) as T.PsString, + femSing: getPassiveStemAspect(root.femPlur, aspect) as T.PsString, + femPlur: getPassiveStemAspect(root.femPlur, aspect) as T.PsString, + }; + } + return concatPsString( + aspect === "imperfective" ? removeAccents(getLong(root)) : getLong(root), + " ", + stativeAux.intransitive.info.stem[aspect] + ); +} + +function getPassiveRootAspect( + root: T.OptionalPersonInflections>, + aspect: T.Aspect +): T.OptionalPersonInflections> { + if ("mascSing" in root) { + return { + mascSing: getPassiveRootAspect( + root.mascSing, + aspect + ) as T.LengthOptions, + mascPlur: getPassiveRootAspect( + root.mascPlur, + aspect + ) as T.LengthOptions, + femPlur: getPassiveRootAspect( + root.femPlur, + aspect + ) as T.LengthOptions, + femSing: getPassiveRootAspect( + root.femPlur, + aspect + ) as T.LengthOptions, + }; + } + const rootR = + aspect === "imperfective" ? removeAccents(root.long) : root.long; + return { + long: concatPsString( + rootR, + " ", + // @ts-ignore + stativeAux.intransitive.info.root[aspect].long + ), // @ts-ignore - return concatPsString(removeAccents(getLong(r)), " ", stativeAux.intransitive.info.participle.past) as T.PsString; + short: concatPsString( + rootR, + " ", + // @ts-ignore + stativeAux.intransitive.info.root[aspect].short + ), + }; } - -function getPassiveStemAspect(root: T.FullForm, aspect: T.Aspect): T.FullForm { - if ("mascSing" in root) { - return { - "mascSing": getPassiveStemAspect(root.mascSing, aspect) as T.PsString, - "mascPlur": getPassiveStemAspect(root.mascPlur, aspect) as T.PsString, - "femSing": getPassiveStemAspect(root.femPlur, aspect) as T.PsString, - "femPlur": getPassiveStemAspect(root.femPlur, aspect) as T.PsString, - }; - } - return concatPsString( - aspect === "imperfective" ? removeAccents(getLong(root)) : getLong(root), - " ", - stativeAux.intransitive.info.stem[aspect], - ); -} - -function getPassiveRootAspect(root: T.OptionalPersonInflections>, aspect: T.Aspect): T.OptionalPersonInflections> { - if ("mascSing" in root) { - return { - "mascSing": getPassiveRootAspect(root.mascSing, aspect) as T.LengthOptions, - "mascPlur": getPassiveRootAspect(root.mascPlur, aspect) as T.LengthOptions, - "femPlur": getPassiveRootAspect(root.femPlur, aspect) as T.LengthOptions, - "femSing": getPassiveRootAspect(root.femPlur, aspect) as T.LengthOptions, - }; - } - const rootR = aspect === "imperfective" ? removeAccents(root.long) : root.long - return { - long: concatPsString( - rootR, - " ", - // @ts-ignore - stativeAux.intransitive.info.root[aspect].long, - ), - // @ts-ignore - short: concatPsString( - rootR, - " ", - // @ts-ignore - stativeAux.intransitive.info.root[aspect].short, - ), - } -} \ No newline at end of file diff --git a/src/types.ts b/src/types.ts index 2fbe948..7c0cd3e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -12,446 +12,503 @@ export type PsStringField = "p" | "f"; export type PsString = { - [k in PsStringField]: string; + [k in PsStringField]: string; } & { - e?: string; + e?: string; +}; +export type PsJSX = { + p: JSX.Element; + f: JSX.Element; + e?: JSX.Element | string; }; -export type PsJSX = { p: JSX.Element, f: JSX.Element, e?: JSX.Element | string }; export type PsWord = PsString & { - hyphen?: HyphenPsContent[], + hyphen?: HyphenPsContent[]; }; -export type HyphenPsContent = { - type: "unwritten", - f: string, -} & Omit | ({ - type: "written", -} & PsString); +export type HyphenPsContent = + | ({ + type: "unwritten"; + f: string; + } & Omit) + | ({ + type: "written"; + } & PsString); export type DictionaryInfo = { - title: string; - license: string; - release: number; - numberOfEntries: number; - url: string; - infoUrl: string; -} + title: string; + license: string; + release: number; + numberOfEntries: number; + url: string; + infoUrl: string; +}; export type Dictionary = { - info: DictionaryInfo; - entries: DictionaryEntry[]; -} + info: DictionaryInfo; + entries: DictionaryEntry[]; +}; export type AllWordsWithInflections = { - info: DictionaryInfo, - words: PsString[], + info: DictionaryInfo; + words: PsString[]; }; // TODO: MAKE THIS A RECORD TYPE -// Record && Record && +// Record && Record && // Partial> && Partial> export type DictionaryEntry = { - // BASE REQUIRED INFO - /** timestamp - used for word id */ - ts: number; - /** Pashto alphabetical index */ - i: number; - /** - * commonality rank - * 0 - wrong - * 1 - historical/not in use - * 2 - rarely used - * 3 - used but there are more common alternatives - * 4 - common - */ - r?: number; - /** entry in Pashto */ - p: string; - /** entry in Phonetics */ - f: string; - /** entry in simplified phonetics */ - g: string; - /** entry in English */ - e: string; + // BASE REQUIRED INFO + /** timestamp - used for word id */ + ts: number; + /** Pashto alphabetical index */ + i: number; + /** + * commonality rank + * 0 - wrong + * 1 - historical/not in use + * 2 - rarely used + * 3 - used but there are more common alternatives + * 4 - common + */ + r?: number; + /** entry in Pashto */ + p: string; + /** entry in Phonetics */ + f: string; + /** entry in simplified phonetics */ + g: string; + /** entry in English */ + e: string; - // PART OF SPEECH AND LINK INFO - /** part of speech info */ - c?: string; - /** link - timestamp of related word */ - l?: number; + // PART OF SPEECH AND LINK INFO + /** part of speech info */ + c?: string; + /** link - timestamp of related word */ + l?: number; - // INFLECTION INFO - /** first masculine irregular inflection in Pashto */ - infap?: string; - /** first masculine irregular inflection in Phonetics */ - infaf?: string; - /** base for second masculine / feminine irregular inflection in Pashto */ - infbp?: string; - /** base for second masculine / feminine irregular inflection in Phonetics */ - infbf?: string; - /** entry does not inflect? */ - noInf?: boolean; + // INFLECTION INFO + /** first masculine irregular inflection in Pashto */ + infap?: string; + /** first masculine irregular inflection in Phonetics */ + infaf?: string; + /** base for second masculine / feminine irregular inflection in Pashto */ + infbp?: string; + /** base for second masculine / feminine irregular inflection in Phonetics */ + infbf?: string; + /** entry does not inflect? */ + noInf?: boolean; - // PLURAL INFO - /** Arabic plural in Pashto */ - app?: string; - /** Arabic plural in Phonetics */ - apf?: string; - /** Pashto irregular plural in Pashto */ - ppp?: string; - /** Pashto irregular plural in phonetics */ - ppf?: string; + // PLURAL INFO + /** Arabic plural in Pashto */ + app?: string; + /** Arabic plural in Phonetics */ + apf?: string; + /** Pashto irregular plural in Pashto */ + ppp?: string; + /** Pashto irregular plural in phonetics */ + ppf?: string; - // VERB INFO - /** imperfective (present) stem in Pashto */ - psp?: string; - /** imperfective (present) stem in Phonetics */ - psf?: string; - /** perfective (subjunctive) stem in Pashto */ - ssp?: string; - /** perfective (subjunctive) stem in Phonetics */ - ssf?: string; - /** perfective root in Pashto */ - prp?: string; - /** perfective root in Phonetics */ - prf?: string; - /** past participle in Pashto */ - pprtp?: string; - /** past participle in Phonetics */ - pprtf?: string; - /** The idiosyncratic third person singular masc. short past in Pashto */ - tppp?: string; - /** The idiosyncratic third person singular masc. short past in Phonetics */ - tppf?: string; - /** intransitive short version is available like ګرځېږي and ګرځي */ - shortIntrans?: boolean; - /** does not take a و - oo perfective verb prefix? */ - noOo?: boolean; - /** takes a seperate و - oo perfective verb prefix? */ - sepOo?: boolean; - /** Pashto separation point for seperable verbs */ - separationAtP?: number; - /** Phonetics separation point for seperable verbs */ - separationAtF?: number; + // VERB INFO + /** imperfective (present) stem in Pashto */ + psp?: string; + /** imperfective (present) stem in Phonetics */ + psf?: string; + /** perfective (subjunctive) stem in Pashto */ + ssp?: string; + /** perfective (subjunctive) stem in Phonetics */ + ssf?: string; + /** perfective root in Pashto */ + prp?: string; + /** perfective root in Phonetics */ + prf?: string; + /** past participle in Pashto */ + pprtp?: string; + /** past participle in Phonetics */ + pprtf?: string; + /** The idiosyncratic third person singular masc. short past in Pashto */ + tppp?: string; + /** The idiosyncratic third person singular masc. short past in Phonetics */ + tppf?: string; + /** intransitive short version is available like ګرځېږي and ګرځي */ + shortIntrans?: boolean; + /** does not take a و - oo perfective verb prefix? */ + noOo?: boolean; + /** takes a seperate و - oo perfective verb prefix? */ + sepOo?: boolean; + /** Pashto separation point for seperable verbs */ + separationAtP?: number; + /** Phonetics separation point for seperable verbs */ + separationAtF?: number; - // PHONETICS - PASHTO - DIACRITICS INFO - /** Is an exception to the rules of diacritics for Pashto/Phonetics */ - diacExcept?: boolean; + // PHONETICS - PASHTO - DIACRITICS INFO + /** Is an exception to the rules of diacritics for Pashto/Phonetics */ + diacExcept?: boolean; - /** the English conjugations of a verb comma seperated set of 5 ie. "see,sees,seeing,saw,seen" or single word ie. "walk" if regular - or the english singular version of a noun */ - ec?: string; - /** the English partical of a English phrasal verb - or the english irregular plural of a noun */ - ep?: string; -} + /** the English conjugations of a verb comma seperated set of 5 ie. "see,sees,seeing,saw,seen" or single word ie. "walk" if regular - or the english singular version of a noun */ + ec?: string; + /** the English partical of a English phrasal verb - or the english irregular plural of a noun */ + ep?: string; +}; -export type DictionaryEntryNoFVars = DictionaryEntry & { __brand: "name for a dictionary entry with all the phonetics variations removed" }; -export type VerbDictionaryEntryNoFVars = VerbDictionaryEntry & { __brand2: "name for a verb dictionary entry with all the phonetics variations removed" }; +export type DictionaryEntryNoFVars = DictionaryEntry & { + __brand: "name for a dictionary entry with all the phonetics variations removed"; +}; +export type VerbDictionaryEntryNoFVars = VerbDictionaryEntry & { + __brand2: "name for a verb dictionary entry with all the phonetics variations removed"; +}; export type VerbEntryNoFVars = { - entry: VerbDictionaryEntryNoFVars, - complement?: DictionaryEntryNoFVars, -} & { __brand: "name for a verb entry with all the phonetics variations removed" }; -export type PsStringNoFVars = PsString & { __brand: "name for a ps string with all the phonetics variations removed" }; -export type FStringNoFVars = string & { __brand: "name for a phonetics string with all the phonetics variations removed" }; - + entry: VerbDictionaryEntryNoFVars; + complement?: DictionaryEntryNoFVars; +} & { + __brand: "name for a verb entry with all the phonetics variations removed"; +}; +export type PsStringNoFVars = PsString & { + __brand: "name for a ps string with all the phonetics variations removed"; +}; +export type FStringNoFVars = string & { + __brand: "name for a phonetics string with all the phonetics variations removed"; +}; export const dictionaryEntryTextFields = [ - "p", - "f", - "e", - "c", - "infap", - "infaf", - "infbp", - "infbf", - "app", - "apf", - "ppp", - "ppf", - "psp", - "psf", - "ssp", - "ssf", - "prp", - "prf", - "pprtp", - "pprtf", - "tppp", - "tppf", - "ec", - "ep", + "p", + "f", + "e", + "c", + "infap", + "infaf", + "infbp", + "infbf", + "app", + "apf", + "ppp", + "ppf", + "psp", + "psf", + "ssp", + "ssf", + "prp", + "prf", + "pprtp", + "pprtf", + "tppp", + "tppf", + "ec", + "ep", ] as const; -export type DictionaryEntryTextField = typeof dictionaryEntryTextFields[number]; +export type DictionaryEntryTextField = + (typeof dictionaryEntryTextFields)[number]; export const dictionaryEntryBooleanFields = [ - "noInf", "shortIntrans", "noOo", "sepOo", "diacExcept", + "noInf", + "shortIntrans", + "noOo", + "sepOo", + "diacExcept", ] as const; export const dictionaryEntryNumberFields = [ - "ts", "r", "i", "l", "separationAtP", "separationAtF", + "ts", + "r", + "i", + "l", + "separationAtP", + "separationAtF", ] as const; -export type DictionaryEntryBooleanField = typeof dictionaryEntryBooleanFields[number]; -export type DictionaryEntryNumberField = typeof dictionaryEntryNumberFields[number]; -export type DictionaryEntryField = DictionaryEntryTextField | DictionaryEntryBooleanField | DictionaryEntryNumberField; +export type DictionaryEntryBooleanField = + (typeof dictionaryEntryBooleanFields)[number]; +export type DictionaryEntryNumberField = + (typeof dictionaryEntryNumberFields)[number]; +export type DictionaryEntryField = + | DictionaryEntryTextField + | DictionaryEntryBooleanField + | DictionaryEntryNumberField; -// TODO: make +// TODO: make export type DictionaryEntryError = { - errors: string[], - p: string, - f: string, - e: string, - ts: number, - erroneousFields: DictionaryEntryField[], -} + errors: string[]; + p: string; + f: string; + e: string; + ts: number; + erroneousFields: DictionaryEntryField[]; +}; export type Spelling = "Afghan" | "Pakistani ی" | "Pakistani ي"; export type TextOptions = { - pTextSize: "normal" | "larger" | "largest"; - phonetics: "lingdocs" | "ipa" | "alalc" | "none"; - dialect: "standard" | "peshawer" | "southern"; - spelling: Spelling; - diacritics: boolean; -} + pTextSize: "normal" | "larger" | "largest"; + phonetics: "lingdocs" | "ipa" | "alalc" | "none"; + dialect: "standard" | "peshawer" | "southern"; + spelling: Spelling; + diacritics: boolean; +}; export enum Person { - FirstSingMale = 0, - FirstSingFemale, - SecondSingMale, - SecondSingFemale, - ThirdSingMale, - ThirdSingFemale, - FirstPlurMale, - FirstPlurFemale, - SecondPlurMale, - SecondPlurFemale, - ThirdPlurMale, - ThirdPlurFemale, + FirstSingMale = 0, + FirstSingFemale, + SecondSingMale, + SecondSingFemale, + ThirdSingMale, + ThirdSingFemale, + FirstPlurMale, + FirstPlurFemale, + SecondPlurMale, + SecondPlurFemale, + ThirdPlurMale, + ThirdPlurFemale, } // INPUT // all information to be passed to conjugating functions export type VerbInfoBase = { - entry: VerbEntry, - transitivity: Transitivity; - yulEnding: boolean | null; - stem: VerbStemSet; - root: VerbRootSet; - participle: ParticipleSet; - idiosyncraticThirdMascSing?: ShortThirdPersFormSet; -} + entry: VerbEntry; + transitivity: Transitivity; + yulEnding: boolean | null; + stem: VerbStemSet; + root: VerbRootSet; + participle: ParticipleSet; + idiosyncraticThirdMascSing?: ShortThirdPersFormSet; +}; export type PassiveRootsAndStems = { - stem: VerbStemSet, - root: VerbRootSet, - participle: { - past: FullForm, - }, -} + stem: VerbStemSet; + root: VerbRootSet; + participle: { + past: FullForm; + }; +}; export type AbilityRootsAndStems = Omit; export type SimpleVerbInfo = VerbInfoBase & { - type: "simple"; -} + type: "simple"; +}; export type StativeCompoundVerbInfo = VerbInfoBase & { - type: "stative compound" - complement: UnisexInflections; -} + type: "stative compound"; + complement: UnisexInflections; +}; export type GenerativeStativeCompoundVerbInfo = VerbInfoBase & { - type: "generative stative compound" - objComplement: ObjComplement, - singularForm?: GenerativeStativeCompoundVerbInfo, - // TODO: Could add intransitive form 🤪 -} + type: "generative stative compound"; + objComplement: ObjComplement; + singularForm?: GenerativeStativeCompoundVerbInfo; + // TODO: Could add intransitive form 🤪 +}; export type DynamicCompoundVerbInfo = VerbInfoBase & { - type: "dynamic compound"; - objComplement: ObjComplement; - auxVerb: DictionaryEntry; - auxVerbComplement?: DictionaryEntry; - singularForm?: DynamicCompoundVerbInfo; - intransitiveForm?: DynamicCompoundVerbInfo; -} + type: "dynamic compound"; + objComplement: ObjComplement; + auxVerb: DictionaryEntry; + auxVerbComplement?: DictionaryEntry; + singularForm?: DynamicCompoundVerbInfo; + intransitiveForm?: DynamicCompoundVerbInfo; +}; export type ObjComplement = { - entry: DictionaryEntry; - plural?: PsString; - person: Person; -} + entry: DictionaryEntry; + plural?: PsString; + person: Person; +}; -export type NonComboVerbInfo = SimpleVerbInfo | - StativeCompoundVerbInfo | DynamicCompoundVerbInfo | GenerativeStativeCompoundVerbInfo; +export type NonComboVerbInfo = + | SimpleVerbInfo + | StativeCompoundVerbInfo + | DynamicCompoundVerbInfo + | GenerativeStativeCompoundVerbInfo; -export type VerbInfo = NonComboVerbInfo | { - type: "transitive or grammatically transitive simple"; - transitive: SimpleVerbInfo; - grammaticallyTransitive: SimpleVerbInfo; -} | { - type: "dynamic or stative compound"; - transitivity: Transitivity; - stative: StativeCompoundVerbInfo; - dynamic: DynamicCompoundVerbInfo; -} | { - type: "dynamic or generative stative compound"; - transitivity: Transitivity; - stative: GenerativeStativeCompoundVerbInfo; - dynamic: DynamicCompoundVerbInfo; -} +export type VerbInfo = + | NonComboVerbInfo + | { + type: "transitive or grammatically transitive simple"; + transitive: SimpleVerbInfo; + grammaticallyTransitive: SimpleVerbInfo; + } + | { + type: "dynamic or stative compound"; + transitivity: Transitivity; + stative: StativeCompoundVerbInfo; + dynamic: DynamicCompoundVerbInfo; + } + | { + type: "dynamic or generative stative compound"; + transitivity: Transitivity; + stative: GenerativeStativeCompoundVerbInfo; + dynamic: DynamicCompoundVerbInfo; + }; -export type Transitivity = "transitive" | "intransitive" | "grammatically transitive"; +export type Transitivity = + | "transitive" + | "intransitive" + | "grammatically transitive"; export type SplitInfo = FullForm<[PsString, PsString]>; export type VerbStemSet = { - perfective: FullForm; - imperfective: FullForm; - perfectiveSplit?: SplitInfo; -} + perfective: FullForm; + imperfective: FullForm; + perfectiveSplit?: SplitInfo; +}; export type VerbRootSet = { - perfective: OptionalPersonInflections>; - imperfective: OptionalPersonInflections>; - perfectiveSplit?: SplitInfo; -} + perfective: OptionalPersonInflections>; + imperfective: OptionalPersonInflections>; + perfectiveSplit?: SplitInfo; +}; export type ParticipleSet = { - present: FullForm, - past: FullForm, -} + present: FullForm; + past: FullForm; +}; export type ShortThirdPersFormSet = { - [K in Aspect]: PsString; -} + [K in Aspect]: PsString; +}; export type Aspect = "perfective" | "imperfective"; export type Length = "short" | "long" | "mini"; export type LengthOptions = { - long: T; - short: T; - mini?: T; -} + long: T; + short: T; + mini?: T; +}; -export type PersonInflectionsField = "mascSing" | "mascPlur" | "femSing" | "femPlur"; -export type OptionalPersonInflections = { - [K in PersonInflectionsField]: T; -} | T; +export type PersonInflectionsField = + | "mascSing" + | "mascPlur" + | "femSing" + | "femPlur"; +export type PersonInflections = { + [K in PersonInflectionsField]: T; +}; +export type OptionalPersonInflections = PersonInflections | T; export type SingleOrLengthOpts = T | LengthOptions; export type VerbConjugation = { - info: NonComboVerbInfo, - // INFINITIVE = info.root.imperfective.long - imperfective: AspectContent; // --╖ ASPECT = "imperfective" - perfective: AspectContent; // --╜ ASPECT = "perfective" - hypothetical: VerbForm; // INFINITIVE - ul + aay - participle: ParticipleContent; - perfect: PerfectContent; // PPART = PAST PARTICIPLE (plus spectial short forms) - passive?: PassiveContent; // only on transitive verbs - singularForm?: VerbConjugation; -} - -export type VerbOutput = VerbConjugation | { - info: VerbInfo, - stative: VerbConjugation, - dynamic: VerbConjugation, -} | { - info: VerbInfo, - transitive: VerbConjugation, - grammaticallyTransitive: VerbConjugation, + info: NonComboVerbInfo; + // INFINITIVE = info.root.imperfective.long + imperfective: AspectContent; // --╖ ASPECT = "imperfective" + perfective: AspectContent; // --╜ ASPECT = "perfective" + hypothetical: VerbForm; // INFINITIVE - ul + aay + participle: ParticipleContent; + perfect: PerfectContent; // PPART = PAST PARTICIPLE (plus spectial short forms) + passive?: PassiveContent; // only on transitive verbs + singularForm?: VerbConjugation; }; +export type VerbOutput = + | VerbConjugation + | { + info: VerbInfo; + stative: VerbConjugation; + dynamic: VerbConjugation; + } + | { + info: VerbInfo; + transitive: VerbConjugation; + grammaticallyTransitive: VerbConjugation; + }; + export type PassiveContent = { - imperfective: AspectContentPassive // --╖ ASPECT = "imperfective" - perfective: AspectContentPassive // --╜ ASPECT = "perfective" - perfect: PerfectContent; // PPART INFINITIVE + kedulStat perfect.pastParticiple - // TODO: ADD PARTICIPLE -} + imperfective: AspectContentPassive; // --╖ ASPECT = "imperfective" + perfective: AspectContentPassive; // --╜ ASPECT = "perfective" + perfect: PerfectContent; // PPART INFINITIVE + kedulStat perfect.pastParticiple + // TODO: ADD PARTICIPLE +}; // ASPECT -> AspectContent export type AspectContent = { - // STEM = info.stem[ASPECT] - // ROOT = info.root[ASPECT] - nonImperative: VerbForm; // STEM + pres ending - future: VerbForm; // به + this.nonImperative - imperative: ImperativeForm; // STEM + imperative ending - past: VerbForm; // ROOT + past ending - habitualPast: VerbForm; // ba + past - modal: ModalContent; -} + // STEM = info.stem[ASPECT] + // ROOT = info.root[ASPECT] + nonImperative: VerbForm; // STEM + pres ending + future: VerbForm; // به + this.nonImperative + imperative: ImperativeForm; // STEM + imperative ending + past: VerbForm; // ROOT + past ending + habitualPast: VerbForm; // ba + past + modal: ModalContent; +}; // ASPECT -> AspectContentPssive export type AspectContentPassive = Omit & { - imperative: undefined, + imperative: undefined; }; export type ModalContent = { - nonImperative: VerbForm; // ROOT + ey + kedulStat.perfective.nonImperative - future: VerbForm; // به + this.nonImperative - past: VerbForm; // ROOT + ey + kedulStat.perfective.past - habitualPast: VerbForm; // ba + past - hypotheticalPast: VerbForm; // ROOT + ey + shw + ey -} + nonImperative: VerbForm; // ROOT + ey + kedulStat.perfective.nonImperative + future: VerbForm; // به + this.nonImperative + past: VerbForm; // ROOT + ey + kedulStat.perfective.past + habitualPast: VerbForm; // ba + past + hypotheticalPast: VerbForm; // ROOT + ey + shw + ey +}; -export type ParticipleForm = SingleOrLengthOpts | SingleOrLengthOpts; +export type ParticipleForm = + | SingleOrLengthOpts + | SingleOrLengthOpts; export type ParticipleContent = { - past: SingleOrLengthOpts | SingleOrLengthOpts, - // TODO: Should this ever have an object matrix?? - present: SingleOrLengthOpts, -} + past: SingleOrLengthOpts | SingleOrLengthOpts; + // TODO: Should this ever have an object matrix?? + present: SingleOrLengthOpts; +}; // PPART -> PerfectContent export type PerfectContent = { - halfPerfect: VerbForm; // PPART - past: VerbForm; // PPART + equative.past - present: VerbForm; // PPART + equative.prest - habitual: VerbForm; // PPART + equative.habit - subjunctive: VerbForm; // PPART + equative.subj - future: VerbForm; // ba + PPART + equative.subj - wouldBe: VerbForm; // ba + PPART + equative.past - pastSubjunctive: VerbForm; // PPART + waay - wouldHaveBeen: VerbForm; // PPART + ba + waay -} + halfPerfect: VerbForm; // PPART + past: VerbForm; // PPART + equative.past + present: VerbForm; // PPART + equative.prest + habitual: VerbForm; // PPART + equative.habit + subjunctive: VerbForm; // PPART + equative.subj + future: VerbForm; // ba + PPART + equative.subj + wouldBe: VerbForm; // ba + PPART + equative.past + pastSubjunctive: VerbForm; // PPART + waay + wouldHaveBeen: VerbForm; // PPART + ba + waay +}; // Plain, 1st, and 2nd Inflection export type InflectionSet = ArrayFixed, 3>; // Plural and Second Inflection -export type PluralInflectionSet = ArrayFixed, 2> +export type PluralInflectionSet = ArrayFixed, 2>; export type Gender = "masc" | "fem"; export type UnisexSet = Record; -export type GenderedSet = UnisexSet | Omit, "fem"> | Omit, "masc">; +export type GenderedSet = + | UnisexSet + | Omit, "fem"> + | Omit, "masc">; export type UnisexInflections = UnisexSet; export type Inflections = GenderedSet; export type PluralInflections = GenderedSet; -export type InflectorOutput = { - arabicPlural: PluralInflections, - plural?: PluralInflections, - bundledPlural?: PluralInflections, - inflections?: Inflections, -} | { - plural: PluralInflections, - arabicPlural?: PluralInflections, - bundledPlural?: PluralInflections, - inflections?: Inflections, -} | { - inflections: Inflections, -} | false; +export type InflectorOutput = + | { + arabicPlural: PluralInflections; + plural?: PluralInflections; + bundledPlural?: PluralInflections; + inflections?: Inflections; + } + | { + plural: PluralInflections; + arabicPlural?: PluralInflections; + bundledPlural?: PluralInflections; + inflections?: Inflections; + } + | { + inflections: Inflections; + } + | false; export type PersonLine = [ - /** singular form of person */ - ArrayOneOrMore, - /** plural form of person */ - ArrayOneOrMore, + /** singular form of person */ + ArrayOneOrMore, + /** plural form of person */ + ArrayOneOrMore ]; /** @@ -460,26 +517,26 @@ export type PersonLine = [ * 1st Person Male, 1st Person Female, 2nd Person Male etc... */ export type VerbBlock = [ - PersonLine, // 1st Person Male - PersonLine, // 1st Person Female - PersonLine, // 2nd Person Male - PersonLine, // 2nd Person Female - PersonLine, // 3rd Person Male - PersonLine, // 3rd Person Female + PersonLine, // 1st Person Male + PersonLine, // 1st Person Female + PersonLine, // 2nd Person Male + PersonLine, // 2nd Person Female + PersonLine, // 3rd Person Male + PersonLine // 3rd Person Female ]; export type EnglishBlock = [ - [string, string], - [string, string], - [string, string], - [string, string], - [string, string], - [string, string], + [string, string], + [string, string], + [string, string], + [string, string], + [string, string], + [string, string] ]; export type ImperativeBlock = [ - PersonLine, // 2nd Person Male - PersonLine, // 2nd Person Female + PersonLine, // 2nd Person Male + PersonLine // 2nd Person Female ]; export type FullForm = OptionalPersonInflections>; @@ -488,48 +545,61 @@ export type ImperativeForm = FullForm; export type SentenceForm = SingleOrLengthOpts>; export interface ArrayFixed extends Array { - 0: T; - length: L; + 0: T; + length: L; } export type Wrapper = T & { __brand: "wrapped" }; export type ArrayOneOrMore = { - 0: T + 0: T; } & T[]; -export type RootsOrStemsToHighlight = ("imperfective root" | "perfective root" | "imperfective stem" | "perfective stem" | "past participle")[]; +export type RootsOrStemsToHighlight = ( + | "imperfective root" + | "perfective root" + | "imperfective stem" + | "perfective stem" + | "past participle" +)[]; /* i.e. ec: ["take", "takes", "taking", "took", "taken"], ep: out */ -export type EnglishVerbConjugationEc = [string, string, string, string, string]; +export type EnglishVerbConjugationEc = [string, string, string, string, string]; export type EnglishVerbConjugation = { - ec: EnglishVerbConjugationEc, - ep: string | undefined, + ec: EnglishVerbConjugationEc; + ep: string | undefined; }; -export type DisplayFormItem = DisplayForm | DisplayFormSubgroup | DisplayFormForSentence; -export type EnglishBuilder = (subject: Person, ec: EnglishVerbConjugationEc, neg: boolean) => string[]; +export type DisplayFormItem = + | DisplayForm + | DisplayFormSubgroup + | DisplayFormForSentence; +export type EnglishBuilder = ( + subject: Person, + ec: EnglishVerbConjugationEc, + neg: boolean +) => string[]; -export type DisplayForm = { - label: string, - aspect?: Aspect, - form: VerbForm | ImperativeForm | ParticipleForm | SentenceForm, - advanced?: boolean, - englishBuilder?: EnglishBuilder, - formula: React.ReactNode, - explanation: React.ReactNode, - sentence?: boolean, - passive?: boolean, - past?: boolean, - reorderWithNegative?: boolean, -} +export type DisplayForm = { + label: string; + aspect?: Aspect; + form: VerbForm | ImperativeForm | ParticipleForm | SentenceForm; + advanced?: boolean; + englishBuilder?: EnglishBuilder; + formula: React.ReactNode; + explanation: React.ReactNode; + sentence?: boolean; + passive?: boolean; + past?: boolean; + reorderWithNegative?: boolean; +}; export type DisplayFormForSentence = Omit & { - form: VerbForm, - secondPronounNeeded?: boolean, -} + form: VerbForm; + secondPronounNeeded?: boolean; +}; -// { +// { // label: string, // aspect?: Aspect, // form: VerbForm, @@ -545,194 +615,238 @@ export type DisplayFormForSentence = Omit & { // } export type DisplayFormSubgroup = { - label: string, - subgroup: string, - advanced?: boolean, - content: DisplayFormItem[], -} + label: string; + subgroup: string; + advanced?: boolean; + content: DisplayFormItem[]; +}; export type AayTail = "ey" | "aay"; -export type NounEntry = DictionaryEntry & { c: string } & { __brand: "a noun entry" }; +export type NounEntry = DictionaryEntry & { c: string } & { + __brand: "a noun entry"; +}; export type MascNounEntry = NounEntry & { __brand2: "a masc noun entry" }; export type FemNounEntry = NounEntry & { __brand2: "a fem noun entry" }; -export type AnimNounEntry = NounEntry & { __brand3: "a anim noun entry" }; -export type UnisexNounEntry = MascNounEntry & { __brand3: "a unisex noun entry" }; -export type UnisexAnimNounEntry = UnisexNounEntry & { __brand4: "an anim unisex noun entry" }; -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 & { c: string } & { __brand: "a verb entry" }; +export type AnimNounEntry = NounEntry & { __brand3: "a anim noun entry" }; +export type UnisexNounEntry = MascNounEntry & { + __brand3: "a unisex noun entry"; +}; +export type UnisexAnimNounEntry = UnisexNounEntry & { + __brand4: "an anim unisex noun entry"; +}; +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 & { c: string } & { + __brand: "a verb entry"; +}; export type VerbEntry = { - entry: VerbDictionaryEntry, - // TODO: the compliment could also be typed? Maybe? - complement?: DictionaryEntry, + entry: VerbDictionaryEntry; + // TODO: the compliment could also be typed? Maybe? + complement?: DictionaryEntry; }; /** A dictionary entry that will include the complement / verb entry structure if it's a verb */ export type FullEntry = VerbEntry | DictionaryEntry; export enum InflectionPattern { - None = 0, - Basic = 1, - UnstressedEy = 2, - StressedEy = 3, - Pashtun = 4, - Squish = 5, - FemInanEe = 6, + None = 0, + Basic = 1, + UnstressedEy = 2, + StressedEy = 3, + Pashtun = 4, + Squish = 5, + FemInanEe = 6, +} + +export type SingularEntry = T & { + __brand7: "a singular noun - as opposed to an always plural noun"; +}; +export type PluralNounEntry = T & { + __brand7: "a noun that is always plural"; }; -export type SingularEntry = T & { __brand7: "a singular noun - as opposed to an always plural noun" }; -export type PluralNounEntry = T & { __brand7: "a noun that is always plural" }; - export type Pattern1Entry = T & { __brand3: "basic inflection pattern" }; -export type Pattern2Entry = T & { __brand3: "ending in unstressed ی pattern" }; +export type Pattern2Entry = T & { + __brand3: "ending in unstressed ی pattern"; +}; export type Pattern3Entry = T & { __brand3: "ending in stressed ی pattern" }; export type Pattern4Entry = T & { __brand3: "Pashtoon pattern" }; export type Pattern5Entry = T & { __brand3: "short squish pattern" }; -export type Pattern6FemEntry = T & { __brand3: "non anim. ending in ي" }; +export type Pattern6FemEntry = T & { + __brand3: "non anim. ending in ي"; +}; export type NonInflecting = T & { __brand3: "non-inflecting" }; export type Entry = NounEntry | AdjectiveEntry | AdverbEntry | VerbEntry; // TODO: make this Rendered with recursive Rendered<> export type VPRendered = { - type: "VPRendered", - king: "subject" | "object", - servant: "subject" | "object" | undefined, - isPast: boolean, - isTransitive: boolean, - isCompound: "stative" | "dynamic" | false, - blocks: Block[][], - kids: Kid[], - englishBase?: string[], - form: FormVersion, - whatsAdjustable: "both" | "king" | "servant", -} + type: "VPRendered"; + king: "subject" | "object"; + servant: "subject" | "object" | undefined; + isPast: boolean; + isTransitive: boolean; + isCompound: "stative" | "dynamic" | false; + blocks: Block[][]; + kids: Kid[]; + englishBase?: string[]; + form: FormVersion; + whatsAdjustable: "both" | "king" | "servant"; +}; -export type VerbTense = "presentVerb" - | "subjunctiveVerb" - | "perfectiveFuture" - | "imperfectiveFuture" - | "perfectivePast" - | "imperfectivePast" - | "habitualPerfectivePast" - | "habitualImperfectivePast"; +export type VerbTense = + | "presentVerb" + | "subjunctiveVerb" + | "perfectiveFuture" + | "imperfectiveFuture" + | "perfectivePast" + | "imperfectivePast" + | "habitualPerfectivePast" + | "habitualImperfectivePast"; export type NounNumber = "singular" | "plural"; -export type EquativeTense = "present" | "subjunctive" | "habitual" | "past" | "future" | "wouldBe" | "pastSubjunctive" | "wouldHaveBeen"; +export type EquativeTense = + | "present" + | "subjunctive" + | "habitual" + | "past" + | "future" + | "wouldBe" + | "pastSubjunctive" + | "wouldHaveBeen"; export type PerfectTense = `${EquativeTense}Perfect`; export type AbilityTense = `${VerbTense}Modal`; export type ImperativeTense = `${Aspect}Imperative`; -export type Tense = EquativeTense | VerbTense | PerfectTense | AbilityTense | ImperativeTense; +export type Tense = + | EquativeTense + | VerbTense + | PerfectTense + | AbilityTense + | ImperativeTense; export type SubjectSelection = { - type: "subjectSelection", - selection: NPSelection | undefined, + type: "subjectSelection"; + selection: NPSelection | undefined; }; export type ObjectSelection = { - type: "objectSelection", - selection: NPSelection | ObjectNP | undefined, + type: "objectSelection"; + selection: NPSelection | ObjectNP | undefined; }; export type SubjectSelectionComplete = { - type: "subjectSelection", - selection: NPSelection, + type: "subjectSelection"; + selection: NPSelection; }; export type PredicateSelectionComplete = { - type: "predicateSelection", - selection: ComplementSelection | NPSelection, + type: "predicateSelection"; + selection: ComplementSelection | NPSelection; }; export type ObjectSelectionComplete = { - type: "objectSelection", - selection: NPSelection | ObjectNP, + type: "objectSelection"; + selection: NPSelection | ObjectNP; }; export type VPSelectionState = { - blocks: VPSBlock[] - verb: VerbSelection, - externalComplement: undefined | UnselectedComplementSelection | ComplementSelection, - form: FormVersion, + blocks: VPSBlock[]; + verb: VerbSelection; + externalComplement: + | undefined + | UnselectedComplementSelection + | ComplementSelection; + form: FormVersion; }; export type VPSelectionComplete = { - blocks: VPSBlockComplete[] - verb: VerbSelectionComplete, - externalComplement: VPSelectionState["externalComplement"], - form: FormVersion, + blocks: VPSBlockComplete[]; + verb: VerbSelectionComplete; + externalComplement: VPSelectionState["externalComplement"]; + form: FormVersion; }; -export type VerbFormName = VerbTense | PerfectTense | AbilityTense | ImperativeTense; +export type VerbFormName = + | VerbTense + | PerfectTense + | AbilityTense + | ImperativeTense; -export type VerbSelectionComplete = Omit & { - tense: VerbFormName, -} +export type VerbSelectionComplete = Omit< + VerbSelection, + "object" | "verbTense" | "perfectTense" | "imperativeTense" | "tenseCategory" +> & { + tense: VerbFormName; +}; export type Voice = "active" | "passive"; export type NewVerbSelection = { - type: "verb", - verb: VerbEntryNoFVars, - dynAuxVerb?: VerbEntryNoFVars, - voice: Voice, - canChangeVoice: boolean, - negative: boolean, - tense: VerbTense | PerfectTense | AbilityTense | ImperativeTense, - transitivity: Transitivity, - canChangeTransGenTrans: boolean, - compound: "stative" | "dynamic" | false, - canChangeStatDyn: boolean, - canBeGenStat: boolean, - variableRs: boolean, -} + type: "verb"; + verb: VerbEntryNoFVars; + dynAuxVerb?: VerbEntryNoFVars; + voice: Voice; + canChangeVoice: boolean; + negative: boolean; + tense: VerbTense | PerfectTense | AbilityTense | ImperativeTense; + transitivity: Transitivity; + canChangeTransGenTrans: boolean; + compound: "stative" | "dynamic" | false; + canChangeStatDyn: boolean; + canBeGenStat: boolean; + variableRs: boolean; +}; export type VerbSelection = { - type: "verb", - verb: VerbEntry, - dynAuxVerb?: VerbEntry, - transitivity: Transitivity, - canChangeTransitivity: boolean, - canChangeStatDyn: boolean, - isCompound: "stative" | "dynamic" | false, - voice: Voice, - canChangeVoice: boolean, - negative: boolean, - verbTense: VerbTense, - perfectTense: PerfectTense, - imperativeTense: ImperativeTense, - tenseCategory: "basic" | "modal" | "perfect" | "imperative", + type: "verb"; + verb: VerbEntry; + dynAuxVerb?: VerbEntry; + transitivity: Transitivity; + canChangeTransitivity: boolean; + canChangeStatDyn: boolean; + isCompound: "stative" | "dynamic" | false; + voice: Voice; + canChangeVoice: boolean; + negative: boolean; + verbTense: VerbTense; + perfectTense: PerfectTense; + imperativeTense: ImperativeTense; + tenseCategory: "basic" | "modal" | "perfect" | "imperative"; }; export type VerbRendered = Omit & { - ps: { - head: PsString | undefined, - rest: SingleOrLengthOpts< - PsString[] - >, - }, - hasBa: boolean, - person: Person, + ps: { + head: PsString | undefined; + rest: SingleOrLengthOpts; + }; + hasBa: boolean; + person: Person; }; -export type VerbObject = - // transitive verb - object not selected yet - undefined | - // transitive verb - obect selected - NPSelection | - // grammatically transitive verb with unspoken 3rd pers masc plur entity - // or intransitive "none" - ObjectNP; +export type VerbObject = + // transitive verb - object not selected yet + | undefined + // transitive verb - obect selected + | NPSelection + // grammatically transitive verb with unspoken 3rd pers masc plur entity + // or intransitive "none" + | ObjectNP; export type NPSelection = { - type: "NP", - selection: NounSelection | PronounSelection | ParticipleSelection, + type: "NP"; + selection: NounSelection | PronounSelection | ParticipleSelection; }; export type APSelection = { - type: "AP", - selection: AdverbSelection | SandwichSelection, + type: "AP"; + selection: AdverbSelection | SandwichSelection; }; export type NPType = "noun" | "pronoun" | "participle"; @@ -740,334 +854,371 @@ export type NPType = "noun" | "pronoun" | "participle"; export type ObjectNP = "none" | Person.ThirdPlurMale; export type PossesorSelection = { - shrunken: boolean, - np: NPSelection, -} + shrunken: boolean; + np: NPSelection; +}; // TODO require/import Person and PsString export type NounSelection = { - type: "noun", - entry: NounEntry, - gender: Gender, - genderCanChange: boolean, - number: NounNumber, - numberCanChange: boolean, - dynamicComplement?: boolean, - adjectives: AdjectiveSelection[], - possesor: undefined | PossesorSelection, - demonstrative: undefined | DemonstrativeSelection, + type: "noun"; + entry: NounEntry; + gender: Gender; + genderCanChange: boolean; + number: NounNumber; + numberCanChange: boolean; + dynamicComplement?: boolean; + adjectives: AdjectiveSelection[]; + possesor: undefined | PossesorSelection; + demonstrative: undefined | DemonstrativeSelection; }; export type DemonstrativeSelection = { - type: "demonstrative", - demonstrative: "daa" | "hagha" | "dagha", - hideNoun: boolean, + type: "demonstrative"; + demonstrative: "daa" | "hagha" | "dagha"; + hideNoun: boolean; }; export type AdverbSelection = { - type: "adverb", - entry: AdverbEntry, -} + type: "adverb"; + entry: AdverbEntry; +}; export type AdjectiveSelection = { - type: "adjective", - entry: AdjectiveEntry, - sandwich: SandwichSelection | undefined, -} + type: "adjective"; + entry: AdjectiveEntry; + sandwich: SandwichSelection | undefined; +}; export type LocativeAdverbSelection = { - type: "loc. adv.", - entry: LocativeAdverbEntry, -} + type: "loc. adv."; + entry: LocativeAdverbEntry; +}; // take an argument for subject/object in rendering English export type PronounSelection = { - type: "pronoun", - person: Person, - distance: "near" | "far", + type: "pronoun"; + person: Person; + distance: "near" | "far"; }; export type ParticipleSelection = { - type: "participle", - verb: VerbEntry, - possesor: undefined | PossesorSelection, + type: "participle"; + verb: VerbEntry; + possesor: undefined | PossesorSelection; }; // not object // type Primitive = string | Function | number | boolean | Symbol | undefined | null; // If T has key K ("user"), replace it -export type ReplaceKey = T extends Record ? (Omit & Record) : T; +export type ReplaceKey = T extends Record + ? Omit & Record + : T; -export type FormVersion = { removeKing: boolean, shrinkServant: boolean }; +export type FormVersion = { removeKing: boolean; shrinkServant: boolean }; // TODO: rendered should would for rendering T.PossesorSelection etc // look recursively down on something export type RenderedPossesorSelection = { - np: Rendered, - shrunken: boolean, + np: Rendered; + shrunken: boolean; }; -export type UnselectedComplementSelection = { type: "complement", selection: { type: "unselected" }}; +export type UnselectedComplementSelection = { + type: "complement"; + selection: { type: "unselected" }; +}; export type Rendered< - T extends - | NPSelection - | NPSelection["selection"] - | APSelection - | APSelection["selection"] - | SubjectSelectionComplete - | ObjectSelectionComplete - | PredicateSelectionComplete - | AdjectiveSelection - | SandwichSelection - | ComplementSelection - | DemonstrativeSelection - | ComplementSelection["selection"] - | UnselectedComplementSelection - | undefined -> = - T extends NPSelection - ? { - type: "NP", - selection: Rendered + T extends + | NPSelection + | NPSelection["selection"] + | APSelection + | APSelection["selection"] + | SubjectSelectionComplete + | ObjectSelectionComplete + | PredicateSelectionComplete + | AdjectiveSelection + | SandwichSelection + | ComplementSelection + | DemonstrativeSelection + | ComplementSelection["selection"] + | UnselectedComplementSelection + | undefined +> = T extends NPSelection + ? { + type: "NP"; + selection: Rendered; } - : T extends APSelection - ? { - type: "AP", - selection: Rendered, + : T extends APSelection + ? { + type: "AP"; + selection: Rendered; } - : T extends ComplementSelection - ? { - type: "complement", - selection: Rendered + : T extends ComplementSelection + ? { + type: "complement"; + selection: Rendered; } - : T extends UnselectedComplementSelection - ? { - type: "complement", - selection: { - type: "unselected", - ps: PsString[], - e: string, - }, + : T extends UnselectedComplementSelection + ? { + type: "complement"; + selection: { + type: "unselected"; + ps: PsString[]; + e: string; + }; } - : T extends SandwichSelection - ? Omit, "inside"> & { - inside: Rendered, + : T extends SandwichSelection + ? Omit, "inside"> & { + inside: Rendered; } - : T extends AdverbSelection - ? { - type: "adverb", - entry: AdverbEntry, - person: Person, - ps: PsString[], - e?: string, + : T extends AdverbSelection + ? { + type: "adverb"; + entry: AdverbEntry; + person: Person; + ps: PsString[]; + e?: string; } - : T extends AdjectiveSelection - ? { - type: "adjective", - entry: AdjectiveEntry, - ps: PsString[], - e?: string, - sandwich: Rendered> | undefined, - inflected: boolean, - person: Person, + : T extends AdjectiveSelection + ? { + type: "adjective"; + entry: AdjectiveEntry; + ps: PsString[]; + e?: string; + sandwich: Rendered> | undefined; + inflected: boolean; + person: Person; } - : T extends DemonstrativeSelection - ? { - type: "demonstrative", - demonstrative: DemonstrativeSelection["demonstrative"], - hideNoun: boolean, - ps: PsString, + : T extends DemonstrativeSelection + ? { + type: "demonstrative"; + demonstrative: DemonstrativeSelection["demonstrative"]; + hideNoun: boolean; + ps: PsString; } - : T extends ComplementSelection - ? { - type: "complement", - selection: Rendered, + : T extends ComplementSelection + ? { + type: "complement"; + selection: Rendered; } - : T extends SubjectSelectionComplete - ? { - type: "subjectSelection", - selection: Rendered, + : T extends SubjectSelectionComplete + ? { + type: "subjectSelection"; + selection: Rendered; } - : T extends ObjectSelectionComplete - ? { - type: "objectSelection", - selection: Rendered | Person.ThirdPlurMale | "none", + : T extends ObjectSelectionComplete + ? { + type: "objectSelection"; + selection: Rendered | Person.ThirdPlurMale | "none"; } - : T extends PredicateSelectionComplete - ? { - type: "predicateSelection", - selection: Rendered | Rendered, - } : T extends undefined - ? { - type: "undefined", - ps: PsString, + : T extends PredicateSelectionComplete + ? { + type: "predicateSelection"; + selection: Rendered | Rendered; } - : ReplaceKey< - Omit, - "e", - string + : T extends undefined + ? { + type: "undefined"; + ps: PsString; + } + : ReplaceKey< + Omit< + T, + | "changeGender" + | "changeNumber" + | "changeDistance" + | "adjectives" + | "possesor" + >, + "e", + string > & { - ps: PsString[], - e?: string, - inflected: boolean, - person: Person, - role: "king" | "servant" | "none", - // TODO: better recursive thing - adjectives?: Rendered[], - possesor?: { - shrunken: boolean, - np: Rendered, - }, - demonstrative?: Rendered, + ps: PsString[]; + e?: string; + inflected: boolean; + person: Person; + role: "king" | "servant" | "none"; + // TODO: better recursive thing + adjectives?: Rendered[]; + possesor?: { + shrunken: boolean; + np: Rendered; + }; + demonstrative?: Rendered; }; export type EPSelectionState = { - blocks: EPSBlock[], - predicate: { - type: "NP" | "Complement", - NP: NPSelection | undefined, - Complement: ComplementSelection | undefined, - }, - equative: EquativeSelection, - omitSubject: boolean, + blocks: EPSBlock[]; + predicate: { + type: "NP" | "Complement"; + NP: NPSelection | undefined; + Complement: ComplementSelection | undefined; + }; + equative: EquativeSelection; + omitSubject: boolean; }; export type EPSBlock = { - key: number, - block: SubjectSelection | APSelection | undefined, + key: number; + block: SubjectSelection | APSelection | undefined; }; export type EPSBlockComplete = { - key: number, - block: SubjectSelectionComplete | APSelection, + key: number; + block: SubjectSelectionComplete | APSelection; }; export type VPSBlock = { - key: number, - // TODO: confusing use of APSelection / should be like APSelection s APSelection complete like the others - block: SubjectSelection | ObjectSelection | (APSelection | undefined) | ComplementSelection, + key: number; + // TODO: confusing use of APSelection / should be like APSelection s APSelection complete like the others + block: + | SubjectSelection + | ObjectSelection + | (APSelection | undefined) + | ComplementSelection; }; export type VPSBlockComplete = { - key: number, - block: SubjectSelectionComplete | ObjectSelectionComplete | APSelection | ComplementSelection, + key: number; + block: + | SubjectSelectionComplete + | ObjectSelectionComplete + | APSelection + | ComplementSelection; }; -export type EPSelectionComplete = Omit & { - blocks: EPSBlockComplete[], - predicate: PredicateSelectionComplete, - omitSubject: boolean, +export type EPSelectionComplete = Omit< + EPSelectionState, + "predicate" | "blocks" +> & { + blocks: EPSBlockComplete[]; + predicate: PredicateSelectionComplete; + omitSubject: boolean; }; -export type ComplementType = "adjective" | "loc. adv." | "sandwich" | "comp. noun"; +export type ComplementType = + | "adjective" + | "loc. adv." + | "sandwich" + | "comp. noun"; export type SandwichSelection = S & { - inside: NPSelection, + inside: NPSelection; }; export type ComplementSelection = { - type: "complement", - selection: AdjectiveSelection - | LocativeAdverbSelection - | SandwichSelection - | NounSelection, + type: "complement"; + selection: + | AdjectiveSelection + | LocativeAdverbSelection + | SandwichSelection + | NounSelection; }; export type Sandwich = { - type: "sandwich", - before: PsString | undefined, - after: PsString | undefined, - e: string, + type: "sandwich"; + before: PsString | undefined; + after: PsString | undefined; + e: string; }; export type EquativeSelection = { - tense: EquativeTense, - negative: boolean, + tense: EquativeTense; + negative: boolean; }; export type EquativeRendered = EquativeSelection & { - ps: SingleOrLengthOpts, - person: Person, - hasBa: boolean, -} + ps: SingleOrLengthOpts; + person: Person; + hasBa: boolean; +}; export type EPRendered = { - type: "EPRendered", - blocks: Block[][], - kids: Kid[], - englishBase?: string[], - omitSubject: boolean, -} + type: "EPRendered"; + blocks: Block[][]; + kids: Kid[]; + englishBase?: string[]; + omitSubject: boolean; +}; -export type EntryFeeder = { - nouns: EntryLookupPortal, - verbs: EntryLookupPortal, - adjectives: EntryLookupPortal, - locativeAdverbs: EntryLookupPortal, - adverbs: EntryLookupPortal, -} | { - nouns: NounEntry[], - verbs: VerbEntry[], - adjectives: AdjectiveEntry[], - locativeAdverbs: LocativeAdverbEntry[], - adverbs: AdverbEntry[], -} +export type EntryFeeder = + | { + nouns: EntryLookupPortal; + verbs: EntryLookupPortal; + adjectives: EntryLookupPortal; + locativeAdverbs: EntryLookupPortal; + adverbs: EntryLookupPortal; + } + | { + nouns: NounEntry[]; + verbs: VerbEntry[]; + adjectives: AdjectiveEntry[]; + locativeAdverbs: LocativeAdverbEntry[]; + adverbs: AdverbEntry[]; + }; -export type EntryFeederSingleType = X[] | EntryLookupPortal; +export type EntryFeederSingleType = + | X[] + | EntryLookupPortal; export type EntryLookupPortal = { - search: (s: string) => X[], - getByTs: (ts: number) => (X | undefined), -} + search: (s: string) => X[]; + getByTs: (ts: number) => X | undefined; +}; -export type EquativeBlock = { type: "equative", equative: EquativeRendered }; +export type EquativeBlock = { type: "equative"; equative: EquativeRendered }; export type Block = { - key: number, - block: | Rendered - | Rendered - | Rendered - | Rendered - | Rendered - | Rendered - | { type: "negative", imperative: boolean } - | EquativeBlock - | VB - | VBE - | VHead, -}; - -export type Kid = { - key: number, - kid: | { type: "ba" } - | MiniPronoun, -} - -export type MiniPronoun = { - type: "mini-pronoun", - person: Person, - ps: PsString, - source: "servant" | "possesive", - np: NPSelection, + key: number; + block: + | Rendered + | Rendered + | Rendered + | Rendered + | Rendered + | Rendered + | { type: "negative"; imperative: boolean } + | EquativeBlock + | VB + | VBE + | VHead; }; +export type Kid = { + key: number; + kid: { type: "ba" } | MiniPronoun; +}; + +export type MiniPronoun = { + type: "mini-pronoun"; + person: Person; + ps: PsString; + source: "servant" | "possesive"; + np: NPSelection; +}; + +export type RenderVerbOutput = { + hasBa: boolean; + vbs: VerbRenderedOutput; + objComp: Rendered | undefined; +}; export type VerbRenderedOutput = [[VHead] | [], [VB, VBE] | [VBE]]; -export type RootsStemsOutput = [[VHead] | [], [VB, VBA] | [VBA]]; // or perfect / equative +export type RootsStemsOutput = [[VHead] | [], [VB, VBA] | [VBA]]; // or perfect / equative export type VB = VBBasic | VBGenNum | Welded | WeldedGN; /** A VB block that can have endings attached to it */ export type VBA = Exclude; /** A VB block that has had a person verb ending attached */ export type VBE = (VBBasic | Welded) & { - person: Person, + person: Person; }; // or equative export type VBNoLenghts = V extends VBBasic - ? Omit & { ps: PsString[] } - : Omit & { right: VBNoLenghts> }; + ? Omit & { ps: PsString[] } + : Omit & { right: VBNoLenghts> }; export type VBBasic = { - type: "VB", - ps: SingleOrLengthOpts, + type: "VB"; + ps: SingleOrLengthOpts; }; // TODO: might be a better design decision to keep the GenderNuber stuff @@ -1075,14 +1226,14 @@ export type VBBasic = { export type VBGenNum = VBBasic & GenderNumber; export type GenderNumber = { - gender: Gender, - number: NounNumber, + gender: Gender; + number: NounNumber; }; export type Welded = { - type: "welded", - left: NComp | VBBasic | Welded, - right: VBBasic, + type: "welded"; + left: NComp | VBBasic | Welded; + right: VBBasic; }; export type WeldedGN = Omit & { right: VBGenNum }; @@ -1091,13 +1242,13 @@ export type VHead = PH | NComp; /** perfective head block */ export type PH = { - type: "PH", - ps: PsString, + type: "PH"; + ps: PsString; }; export type NComp = { - type: "NComp", - comp: Comp, + type: "NComp"; + comp: Comp; }; // element can be one of @@ -1105,16 +1256,16 @@ export type NComp = { // - locative adv // - sandwich (TODO) // - noun -export type Comp = AdjComp | OtherComp +export type Comp = AdjComp | OtherComp; export type AdjComp = { - type: "AdjComp", - ps: PsString, - number: NounNumber, - gender: Gender, + type: "AdjComp"; + ps: PsString; + number: NounNumber; + gender: Gender; }; export type OtherComp = { - type: "Comp", - ps: PsString, + type: "Comp"; + ps: PsString; }; diff --git a/yarn.lock b/yarn.lock index f485055..40bb1da 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5425,6 +5425,11 @@ forwarded@0.2.0: resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== +fp-ts@^2.16.0: + version "2.16.0" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-2.16.0.tgz#64e03314dfc1c7ce5e975d3496ac14bc3eb7f92e" + integrity sha512-bLq+KgbiXdTEoT1zcARrWEpa5z6A/8b7PcDW7Gef3NSisQ+VS7ll2Xbf1E+xsgik0rWub/8u0qP/iTTjj+PhxQ== + fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz" @@ -9539,6 +9544,11 @@ react-lifecycles-compat@^3.0.4: resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== +react-media-hook@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/react-media-hook/-/react-media-hook-0.5.0.tgz#f830231f31ea80049f8cbaf8058da90ab71e7150" + integrity sha512-OupDgOSCjUUWPiXq3HMoRwpsQry4cGf4vKzh2E984Xtm4I01ZFbq8JwCG/RPqXB9h0qxgzoYLbABC+LIZH8deQ== + react-overlays@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/react-overlays/-/react-overlays-5.1.1.tgz"