more work, still need to get dynamic compounds working on new verb engine

This commit is contained in:
adueck 2023-07-19 16:12:49 +04:00
parent 68d83d95d4
commit 5f8c4ba876
22 changed files with 8343 additions and 5477 deletions

5
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"typescript.preferences.autoImportFileExcludePatterns": [
"../../library.ts"
],
}

View File

@ -76,6 +76,8 @@
]
},
"dependencies": {
"fp-ts": "^2.16.0",
"react-media-hook": "^0.5.0",
"react-select": "^5.4.0"
}
}

View File

@ -27,24 +27,37 @@ const persInfs: {
{
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 <div className="my-2" style={{ display: "flex", flexDirection: "row", alignItems: "center", justifyContent: "center" }}>
return (
<div
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
}}
>
<div className="mr-2">
When the <strong>{props.transitivity === "intransitive" ? "subject" : "object"}</strong> is
When the <strong>{props.subjOrObj}</strong> is
</div>
<div>
<select className="form-control form-control-sm d-inline-block" value={props.persInf} id="verb_info_pers_select" onChange={hChange}>
<select
className="form-control form-control-sm d-inline-block"
value={props.persInf}
id="verb_info_pers_select"
onChange={hChange}
>
{persInfs.map((pers) => (
<option key={pers.value} value={pers.value}>
{pers.label}
@ -52,7 +65,8 @@ function PersInfsPicker(props: {
))}
</select>
</div>
</div>;
</div>
);
}
export default PersInfsPicker;

View File

@ -3,15 +3,32 @@ import * as T from "../../types";
import Pashto from "./Pashto";
import Phonetics from "./Phonetics";
const arrowDown = <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" className="bi bi-caret-down" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M3.204 5L8 10.481 12.796 5H3.204zm-.753.659l4.796 5.48a1 1 0 0 0 1.506 0l4.796-5.48c.566-.647.106-1.659-.753-1.659H3.204a1 1 0 0 0-.753 1.659z"/>
</svg>;
const arrowDown = (
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
fill="currentColor"
className="bi bi-caret-down"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M3.204 5L8 10.481 12.796 5H3.204zm-.753.659l4.796 5.48a1 1 0 0 0 1.506 0l4.796-5.48c.566-.647.106-1.659-.753-1.659H3.204a1 1 0 0 0-.753 1.659z"
/>
</svg>
);
function TableCell({ item, textOptions, center, noBorder }: {
item: T.ArrayOneOrMore<T.PsString>,
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]);
@ -20,14 +37,16 @@ function TableCell({ item, textOptions, center, noBorder }: {
}
const w = item[version] || item[0];
return (
<td style={{ ...noBorder ? { border: "none" } : {} }}>
<div style={{
<td style={{ ...(noBorder ? { border: "none" } : {}) }}>
<div
style={{
display: "flex",
flexDirection: "row",
flexWrap: "wrap",
justifyContent: center ? "center" : "space-between",
alignItems: "center",
}}>
}}
>
<div>
<div>
<Pashto opts={textOptions}>{w}</Pashto>
@ -35,15 +54,25 @@ function TableCell({ item, textOptions, center, noBorder }: {
<div>
<Phonetics opts={textOptions}>{w}</Phonetics>
</div>
{w.e && (Array.isArray(w.e)
? w.e.map(e => <div key={e} className="text-muted small">{e}</div>)
: <div className="text-muted">{w.e}</div>)}
{w.e &&
(Array.isArray(w.e) ? (
w.e.map((e) => (
<div key={e} className="text-muted small">
{e}
</div>
{item.length > 1 &&
<button className="btn btn-sm btn-light mx-2 my-2" onClick={advanceVersion}>
))
) : (
<div className="text-muted">{w.e}</div>
))}
</div>
{item.length > 1 && (
<button
className="btn btn-sm btn-light mx-2 my-2"
onClick={advanceVersion}
>
ver. {version + 1}/{item.length} {arrowDown}
</button>
}
)}
</div>
</td>
);

View File

@ -16,46 +16,67 @@ import {
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 {
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)
const agreesWith =
info.transitivity !== "intransitive" &&
displayForm.past &&
!displayForm.passive
? "object"
: "subject";
const extraExplanation = (!displayForm.past)
const extraExplanation = !displayForm.past
? ""
: (info.transitivity === "grammatically transitive")
: 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 <><strong>Note:</strong> {beginning}<strong>{agreesWith}</strong>{extraExplanation}</>
: 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 (
<>
<strong>Note:</strong> {beginning}
<strong>{agreesWith}</strong>
{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<T.PersonInflectionsField>("mascSing");
const [length, setLength] = useState<T.Length>(defaultLength);
const [showingExplanation, setShowingExplanation] = useState<boolean>(false);
const block = "label" in displayForm ? displayForm.form : displayForm;
const chosenPersInf = "mascSing" in block
? block[persInf]
: block;
const form = "long" in chosenPersInf
const chosenPersInf = "mascSing" in block ? block[persInf] : block;
const form =
"long" in chosenPersInf
? chosenPersInf[length] || chosenPersInf.short
: chosenPersInf;
useEffect(() => {
@ -69,51 +90,80 @@ function VerbFormDisplay({ displayForm, textOptions, info, showingFormInfo, engl
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) && <p className="small text-muted">
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) && (
<p className="small text-muted">
{agreementInfo(info, displayForm)}
</p>}
</p>
)}
<div className="mb-1 d-flex justify-content-between align-items-center">
<samp>Formula: {displayForm.formula}</samp>
<button className="btn btn-sm btn-outline-secondary text-nowrap ml-2" onClick={() => setShowingExplanation(!showingExplanation)}>
<i className={`fas fa-caret-${showingExplanation ? "down" : "right"}`} /> Meaning
<button
className="btn btn-sm btn-outline-secondary text-nowrap ml-2"
onClick={() => setShowingExplanation(!showingExplanation)}
>
<i
className={`fas fa-caret-${
showingExplanation ? "down" : "right"
}`}
/>{" "}
Meaning
</button>
</div>
{showingExplanation && <div className="my-2">
{displayForm.explanation}
</div>}
</>}
{"long" in chosenPersInf &&
{showingExplanation && (
<div className="my-2">{displayForm.explanation}</div>
)}
</>
)}
{"long" in chosenPersInf && (
<div className="text-center">
<ButtonSelect
small
options={[
{ label: "Long", value: "long" },
{ label: "Short", value: "short" },
..."mini" in chosenPersInf ? [{
label: "Mini", value: "mini",
}] : [],
...("mini" in chosenPersInf
? [
{
label: "Mini",
value: "mini",
},
]
: []),
]}
value={length}
handleChange={(p) => setLength(p as T.Length)}
/>
</div>
}
{("mascSing" in block && info) && <PersonInfsPicker
)}
{"mascSing" in block && info && (
<PersonInfsPicker
persInf={persInf}
handleChange={(p) => setPersInf(p)}
transitivity={info.transitivity}
/>}
{"masc" in form ?
subjOrObj={info.transitivity === "transitive" ? "object" : "subject"}
/>
)}
{"masc" in form ? (
<InflectionsTable inf={form} textOptions={textOptions} />
: "p" in form ?
<SingleItemDisplay item={form} english={english} textOptions={textOptions} />
:
) : "p" in form ? (
<SingleItemDisplay
item={form}
english={english}
textOptions={textOptions}
/>
) : (
<VerbTable block={form} english={english} textOptions={textOptions} />
}
)}
</>
);
}
export default VerbFormDisplay;

View File

@ -6,10 +6,7 @@
*
*/
import {
CSSProperties,
useState,
} from "react";
import { CSSProperties, useState } from "react";
import {
pickPersInf,
hasPersInfs,
@ -39,34 +36,52 @@ const title: CSSProperties = {
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 hasPerfectiveSplit =
"perfectiveSplit" in info.root &&
"perfectiveSplit" in info.stem &&
!!(info.root.perfectiveSplit || info.stem.perfectiveSplit);
const showPersInf = hasPersInfs(info);
const [persInf, setPersInf] = useState<T.PersonInflectionsField>("mascSing");
const [split, setSplit] = useState<boolean>(false);
const perfectiveStem = (info.stem.perfectiveSplit && split)
const perfectiveStem =
info.stem.perfectiveSplit && split
? info.stem.perfectiveSplit
: info.stem.perfective;
const perfectiveRoot = (info.root.perfectiveSplit && split)
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 (
<div className="mt-3">
{showPersInf && <PersonInfsPicker
{showPersInf && (
<PersonInfsPicker
persInf={persInf}
handleChange={(p) => setPersInf(p)}
transitivity={"transitivity" in info ? info.transitivity : "intransitive"}
/>}
<div className="verb-info" style={{
subjOrObj={
"transitivity" in info && info.transitivity === "intransitive"
? "subject"
: "object"
}
/>
)}
<div
className="verb-info"
style={{
textAlign: "center",
maxWidth: "500px",
margin: "0 auto",
@ -74,7 +89,8 @@ export function RootsAndStems({ textOptions, info, hidePastParticiple, highlight
backgroundRepeat: "no-repeat",
backgroundPosition: hidePastParticiple ? "50% 45%" : "50% 35%",
backgroundSize: "50%",
}}>
}}
>
{/* <div style={{
fontSize: "larger",
}}>
@ -85,11 +101,14 @@ export function RootsAndStems({ textOptions, info, hidePastParticiple, highlight
{info.subDef}
</div>
} */}
<div style={{
<div
style={{
border: "2px solid black",
padding: "1rem",
margin: "0.25rem",
}} className="container">
}}
className="container"
>
<div className={rowClass + " align-items-center"}>
<div className={colClass}>
<i className="fas fa-video fa-lg" />
@ -99,19 +118,26 @@ export function RootsAndStems({ textOptions, info, hidePastParticiple, highlight
<div>
<i className="fas fa-camera fa-lg mx-3" />
</div>
{hasPerfectiveSplit && <div>
{hasPerfectiveSplit && (
<div>
<button
className="btn btn-sm btn-outline-secondary"
onClick={() => setSplit(!split)}
>
{split ? "join" : "split"} head
</button>
</div>}
</div>
)}
</div>
</div>
</div>
<div className={rowClass}>
<div className={colClass} style={highlighted?.includes("imperfective stem") ? highlight : {}}>
<div
className={colClass}
style={
highlighted?.includes("imperfective stem") ? highlight : {}
}
>
<div style={title as any}>
<div>Imperfective Stem</div>
</div>
@ -123,7 +149,10 @@ export function RootsAndStems({ textOptions, info, hidePastParticiple, highlight
/>
</div>
</div>
<div className={colClass} style={highlighted?.includes("perfective stem") ? highlight : {}}>
<div
className={colClass}
style={highlighted?.includes("perfective stem") ? highlight : {}}
>
<div style={title as any}>
<div>Perfective Stem</div>
</div>
@ -137,7 +166,12 @@ export function RootsAndStems({ textOptions, info, hidePastParticiple, highlight
</div>
</div>
<div className={rowClass}>
<div className={colClass} style={highlighted?.includes("imperfective root") ? highlight : {}}>
<div
className={colClass}
style={
highlighted?.includes("imperfective root") ? highlight : {}
}
>
<div style={title as any}>
<div>Imperfective Root</div>
</div>
@ -148,7 +182,10 @@ export function RootsAndStems({ textOptions, info, hidePastParticiple, highlight
/>
</div>
</div>
<div className={colClass} style={highlighted?.includes("perfective root") ? highlight : {}}>
<div
className={colClass}
style={highlighted?.includes("perfective root") ? highlight : {}}
>
<div>
<div style={title as any}>
<div>Perfective Root</div>
@ -162,35 +199,45 @@ export function RootsAndStems({ textOptions, info, hidePastParticiple, highlight
</div>
</div>
</div>
{!hidePastParticiple && "participle" in info &&<div className="text-center" style={highlighted?.includes("past participle") ? highlight : {}}>
{!hidePastParticiple && "participle" in info && (
<div
className="text-center"
style={highlighted?.includes("past participle") ? highlight : {}}
>
<div style={title as any}>Past Participle</div>
<VerbInfoItemDisplay
item={pickPersInf(info.participle.past, persInf)}
textOptions={textOptions}
/>
</div>}
</div>
)}
</div>
</div>
</div>
);
}
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,
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 (
<div className="my-3">
{!hideTypeInfo && <VerbTypeInfo
info={info}
textOptions={textOptions}
/>}
{!hideTypeInfo && <VerbTypeInfo info={info} textOptions={textOptions} />}
<Hider
showing={showingStemsAndRoots}
label={`🌳 Roots and Stems for ${inf.p}`}

View File

@ -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 }) {
function AllTensesDisplay({
VS,
opts,
}: {
VS: T.VerbSelection;
opts: T.TextOptions;
}) {
const [showing, setShowing] = useStickyState<string[]>([], "VPTensesShowing");
const [showFormulas, setShowFormulas] = useStickyState<boolean>(false, "showFormulasWithCharts");
const [showFormulas, setShowFormulas] = useStickyState<boolean>(
false,
"showFormulasWithCharts"
);
const adjustShowing = (v: string) => {
if (showing.includes(v)) {
setShowing(os => os.filter(x => x !== v));
setShowing((os) => os.filter((x) => x !== v));
} else {
setShowing(os => [v, ...os]);
setShowing((os) => [v, ...os]);
}
}
const options = VS.tenseCategory === "basic"
};
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;
// 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 <div>
<div className="clickable mb-2 small text-center" onClick={() => setShowFormulas(x => !x)}>
return (
<div>
<div
className="clickable mb-2 small text-center"
onClick={() => setShowFormulas((x) => !x)}
>
🧪 {!showFormulas ? "Show" : "Hide"} Formulas
</div>
{options.map((tense) => <div key={Math.random()}>
{options.map((tense) => {
const t = getTense(tense.value);
return (
<div key={Math.random()}>
<Hider
label={tense.label}
showing={showing.includes(tense.value)}
handleChange={() => adjustShowing(tense.value)}
hLevel={5}
>
{showFormulas && <div className="mb-1">
{showFormulas && (
<div className="mb-1">
<samp>{tense.formula}</samp>
</div>}
</div>
)}
{showing && (
<ChartDisplay
conjugations={conjugations}
tense={getTense(tense.value)}
verb={VS.verb}
negative={VS.negative}
tense={t}
transitivity={VS.transitivity}
voice={VS.voice}
imperative={isImperativeTense(t)}
opts={opts}
/>
)}
</Hider>
</div>)}
</div>;
</div>
);
})}
</div>
);
}
export default AllTensesDisplay;

View File

@ -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: <i className="mx-1 fas fa-crown" />,
servant: <i className="mx-1 fas fa-male" />,
};
function VerbChartDisplay({
chart,
opts,
shortDefault,
transitivity,
past,
}: {
chart: T.OptionalPersonInflections<
T.SingleOrLengthOpts<T.RenderVerbOutput[]>
>;
opts: T.TextOptions;
shortDefault?: boolean;
transitivity: T.Transitivity;
past: boolean;
}) {
const [length, setLength] = useState<T.Length>(
shortDefault ? "short" : "long"
);
const [persInf, setPersInf] = useState<T.PersonInflectionsField>("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 (
<>
<div className="mb-2">
{"mascSing" in chart ? (
<PersInfsPicker
persInf={persInf}
handleChange={setPersInf}
subjOrObj="object"
/>
) : (
<div />
)}
</div>
<div className="row">
<div className="col">
<AgreementInfo transitivity={transitivity} past={past} />
</div>
<div className="col">
{"long" in chartWPers && (
<LengthSelection
hasMini={"mini" in chartWPers}
value={length}
onChange={setLength}
/>
)}
</div>
{desktop && <div className="col" />}
</div>
<table className="table mt-2" style={{ tableLayout: "fixed" }}>
<thead>
<tr>
<th scope="col" style={{ width: "3rem" }}>
Pers.
</th>
<th scope="col">Singular</th>
<th scope="col">Plural</th>
</tr>
</thead>
<tbody>
{verbBlock.map((personRow, i) => (
<PersonRow
key={Math.random()}
// get proper version for imperative blocks
person={verbBlock.length === 1 ? 1 : i}
item={personRow}
opts={opts}
/>
))}
</tbody>
</table>
</>
);
}
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 (
<GenderedPersonRow
person={person}
line={item[0]}
opts={opts}
gender={undefined}
/>
);
}
return (
<>
<GenderedPersonRow
person={person}
line={item[0]}
opts={opts}
gender="masc"
/>
<GenderedPersonRow
person={person}
line={item[1]}
opts={opts}
gender="fem"
/>
</>
);
}
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 (
<tr key={`${person}${gender}`}>
<th scope="row" style={{ color }}>
{rowLabel}
</th>
<TableCell item={line[0]} textOptions={opts} />
<TableCell item={line[1]} textOptions={opts} />
</tr>
);
}
function LengthSelection({
value,
onChange,
hasMini,
}: {
hasMini: boolean;
value: T.Length;
onChange: (v: T.Length) => void;
}) {
return (
<div className="text-center">
<ButtonSelect
small
options={[
{ label: "Long", value: "long" },
{ label: "Short", value: "short" },
...(hasMini
? [
{
label: "Mini",
value: "mini",
},
]
: []),
]}
value={value}
handleChange={(p) => onChange(p as T.Length)}
/>
</div>
);
}
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 (
<div className="text-muted small mt-1">
{roleIcon.king} agrees w/{" "}
<strong>
{transitivity !== "intransitive" && past ? "object" : "subject"}
</strong>
</div>
);
}
export default VerbChartDisplay;

View File

@ -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 <div className="mb-4">
<VerbFormDisplay
displayForm={form}
showingFormInfo={false}
textOptions={opts}
info={conjugations.info}
const verbChart = buildVerbChart({
verb,
tense,
voice,
negative,
transitivity,
imperative,
});
return (
<div className="mb-4">
<NewVerbFormDisplay
chart={verbChart}
opts={opts}
transitivity={transitivity}
past={isPastTense(tense)}
/>
</div>;
</div>
);
}
export default ChartDisplay;

View File

@ -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<T.SingleOrLengthOpts<T.RenderVerbOutput[]>> {
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<T.RenderVerbOutput[]> {
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<V extends T.VB | T.VBE>(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<T.RenderVerbOutput[]> {
if (!hasLengths) {
return ps;
}
const wLengths: T.LengthOptions<T.RenderVerbOutput[]> = {
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.SingleOrLengthOpts<T.RenderVerbOutput[]>>
): T.OptionalPersonInflections<T.SingleOrLengthOpts<T.RenderVerbOutput[]>> {
const toCheck = [
"femSing",
"femPlur",
"mascPlur",
] as T.PersonInflectionsField[];
const diffExists = toCheck.some((f) => !equals(v[f], v.mascSing));
return diffExists ? v : v.mascSing;
}

43
src/lib/src/fp-ps.ts Normal file
View File

@ -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<T.PsString> = struct({
p: S.Eq,
f: S.Eq,
});
export const eqPsStringWVars: Eq<T.PsString[]> = {
equals: (x, y) => {
return (
x.length === y.length && x.every((a, i) => eqPsString.equals(a, y[i]))
);
},
};
export const semigroupPsStringWVars: Semigroup<T.PsString[]> = {
concat: (x, y) =>
x.flatMap((a) => y.map((b) => semigroupPsString.concat(a, b))),
};
export const semigroupPsString: Semigroup<T.PsString> = {
concat: (x, y) => ({
p: x.p + y.p,
f: x.f + y.f,
}),
};
export const monoidPsString: Monoid<T.PsString> = {
concat: semigroupPsString.concat,
empty: {
p: "",
f: "",
},
};
export const monoidPsStringWVars: Monoid<T.PsString[]> = {
concat: semigroupPsStringWVars.concat,
empty: [monoidPsString.empty],
};

View File

@ -26,7 +26,9 @@ export function entryOfFull(e: T.FullEntry): T.DictionaryEntry {
}
// just for type safety
export function noPersInfs<S extends object>(s: T.OptionalPersonInflections<S>): S {
export function noPersInfs<S extends object>(
s: T.OptionalPersonInflections<S>
): S {
if ("mascSing" in s) {
// this path shouldn't be used, just for type safety
return s.mascSing;
@ -35,14 +37,13 @@ export function noPersInfs<S extends object>(s: T.OptionalPersonInflections<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<T>(s: T.OptionalPersonInflections<T>, persInf: T.PersonInflectionsField): T {
export function pickPersInf<T>(
s: T.OptionalPersonInflections<T>,
persInf: T.PersonInflectionsField
): T {
// @ts-ignore
if ("mascSing" in s) {
return s[persInf];
@ -82,7 +83,9 @@ export function getFirstSecThird(p: T.Person): 1 | 2 | 3 {
// return s;
// }
export function hasPersInfs(info: T.NonComboVerbInfo | T.PassiveRootsAndStems | T.AbilityRootsAndStems): boolean {
export function hasPersInfs(
info: T.NonComboVerbInfo | T.PassiveRootsAndStems | T.AbilityRootsAndStems
): boolean {
if ("participle" in info) {
return (
"mascSing" in info.root.perfective ||
@ -92,15 +95,16 @@ export function hasPersInfs(info: T.NonComboVerbInfo | T.PassiveRootsAndStems |
);
}
return (
"mascSing" in info.root.perfective ||
"mascSing" in info.stem.perfective
"mascSing" in info.root.perfective || "mascSing" in info.stem.perfective
);
}
// TODO: deprecated using new verb rendering thing
export function chooseParticipleInflection(
pPartInfs: T.SingleOrLengthOpts<T.UnisexInflections> | T.SingleOrLengthOpts<T.PsString>,
person: T.Person,
pPartInfs:
| T.SingleOrLengthOpts<T.UnisexInflections>
| T.SingleOrLengthOpts<T.PsString>,
person: T.Person
): T.SingleOrLengthOpts<T.PsString> {
if ("long" in pPartInfs) {
return {
@ -116,7 +120,10 @@ export function chooseParticipleInflection(
return pPartInfs; // already just one thing
}
export function getPersonNumber(gender: T.Gender, number: T.NounNumber): T.Person {
export function getPersonNumber(
gender: T.Gender,
number: T.NounNumber
): T.Person {
const base = gender === "masc" ? 4 : 5;
return base + (number === "singular" ? 0 : 6);
}
@ -125,8 +132,12 @@ export function personFromVerbBlockPos(pos: [number, number]): T.Person {
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<T.PsString>): boolean {
@ -139,21 +150,28 @@ export function spaceInForm(form: T.FullForm<T.PsString>): boolean {
return form.p.includes(" ");
}
export function getPersonFromVerbForm(form: T.SingleOrLengthOpts<T.VerbBlock>, person: T.Person): T.SentenceForm {
return fmapSingleOrLengthOpts(x => {
export function getPersonFromVerbForm(
form: T.SingleOrLengthOpts<T.VerbBlock>,
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;
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" {
export function getAuxTransitivity(
trans: T.Transitivity
): "transitive" | "intransitive" {
return trans === "intransitive" ? "intransitive" : "transitive";
}
@ -169,29 +187,35 @@ export function personIsPlural(person: T.Person): boolean {
return person > 5;
}
export function getEnglishPersonInfo(person: T.Person, version?: "short" | "long"): string {
const p = ([0,1,6,7].includes(person)
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 n = version === "short" ? (number === "plur" ? "pl" : "sg") : number;
const gender = personGender(person);
const g = version === "short"
? (gender === "masc" ? "m" : "f")
: gender;
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),
@ -199,23 +223,29 @@ export function personToGenNum(p: T.Person): {
};
}
export function getEnglishParticipleInflection(person: T.Person, version?: "short" | "long"): string {
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 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<M>(arr: M[]): M {
return arr[
Math.floor(Math.random()*arr.length)
];
export function randFromArray<M>(arr: Readonly<M[]>): M {
return arr[Math.floor(Math.random() * arr.length)];
}
export const isFirstPerson = (p: T.Person) => [0, 1, 6, 7].includes(p);
@ -233,11 +263,17 @@ export function isSentenceForm(f: any): boolean {
return Array.isArray(f) && "p" in f[0];
}
export function isNounAdjOrVerb(entry: T.DictionaryEntry): "nounAdj" | "verb" | false {
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.")) {
if (
entry.c.includes("adj.") ||
entry.c.includes("n. m.") ||
entry.c.includes("n. f.")
) {
return "nounAdj";
}
if (entry.c.slice(0, 3) === "v. ") {
@ -274,7 +310,7 @@ export function parseEc(ec: string): T.EnglishVerbConjugationEc {
if (s === "be") {
return ["am", "is", "being", "was", "been"];
}
if ((s.slice(-1) === "y") && !isVowel(s.slice(-2)[0])) {
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`];
}
@ -285,22 +321,21 @@ export function parseEc(ec: string): T.EnglishVerbConjugationEc {
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;
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)
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.length === 5
? [items[0], items[1], items[2], items[3], items[4]]
: makeRegularConjugations(items[0]);
}
export function chooseLength<N>(x: T.SingleOrLengthOpts<N>, length: "long" | "short"): N {
export function chooseLength<N>(
x: T.SingleOrLengthOpts<N>,
length: "long" | "short"
): N {
// @ts-ignore
if ("long" in x) {
return x[length];

File diff suppressed because it is too large Load Diff

View File

@ -2,119 +2,125 @@ import * as T from "../../../types";
import {
getVerbBlockPosFromPerson,
isSecondPerson,
personGender,
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,
} 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<T.VerbTense | T.ImperativeTense, {
aspect: T.Aspect,
tenseC: "past" | "present" | "imperative",
hasBa: boolean,
}> = {
"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,
} {
// 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 });
return renderPerfectVerb({
verb,
tense,
voice,
person: object ?? subject,
});
}
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({
@ -123,20 +129,22 @@ export function renderVerb({ verb, tense: tense, person, voice, negative, comple
aspect,
voice,
type,
genderNumber: complementGenNum,
genderNumber: personToGenNum(transitive ? object : subject),
});
// #2 add the verb ending to it
const ending = getEnding(person, tenseC, aspect);
const ending = getEnding(king, tenseC, aspect);
return {
hasBa,
objComp: undefined,
vbs: [
vHead,
addEnding({
rs: rest,
ending,
verb,
person,
pastThird: isPast && person === T.Person.ThirdSingMale,
person: king,
pastThird: isPast && king === T.Person.ThirdSingMale,
aspect,
basicForm: type === "basic" && voice === "active",
}),
@ -144,13 +152,21 @@ export function renderVerb({ verb, tense: tense, person, voice, negative, comple
};
}
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]] } {
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<T.NPSelection> | undefined;
} {
const hasBa = perfectTenseHasBa(tense);
// #1 get the past participle
const pp = getPastParticiple(verb, voice, personToGenNum(person));
@ -160,27 +176,39 @@ function renderPerfectVerb({ tense, verb, voice, person }: {
const equativeBlock: T.VBE = {
type: "VB",
person,
ps: fmapSingleOrLengthOpts(x => x[row][col], equative),
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<T.PsString[]>,
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<T.PsString[]>;
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.PsString[]>): T.VBE {
function addEnd(
vba: T.VBA,
ending: T.SingleOrLengthOpts<T.PsString[]>
): T.VBE {
if (vba.type === "welded") {
return {
...vba,
@ -191,9 +219,12 @@ function addEnding({ verb, rs, ending, person, pastThird, aspect, basicForm }: {
return {
...addToVBBasicEnd(vba, ending),
person,
};
}
}
function addToVBBasicEnd(vb: T.VBBasic, end: T.SingleOrLengthOpts<T.PsString[]>): T.VBBasic {
function addToVBBasicEnd(
vb: T.VBBasic,
end: T.SingleOrLengthOpts<T.PsString[]>
): T.VBBasic {
if ("long" in vb.ps) {
if (vb.ps.short[0].f === "ghl" && pastThird && basicForm) {
return {
@ -208,18 +239,23 @@ function addEnding({ verb, rs, ending, person, pastThird, aspect, basicForm }: {
...vb,
ps: {
long: verbEndingConcat(vb.ps.long, endLong),
short: pastThird && basicForm
short:
pastThird && basicForm
? ensure3rdPast(vb.ps.short, endShort, verb, aspect)
: verbEndingConcat(vb.ps.short, endShort),
...vb.ps.mini ? {
...(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");
throw new Error(
"should not be using verb stems with long and short endings"
);
}
return {
...vb,
@ -228,7 +264,11 @@ function addEnding({ verb, rs, ending, person, pastThird, aspect, basicForm }: {
}
}
function getEnding(person: T.Person, tenseC: "present" | "past" | "imperative", aspect: T.Aspect) {
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");
@ -236,18 +276,25 @@ function getEnding(person: T.Person, tenseC: "present" | "past" | "imperative",
const number = personNumber(person);
const ends = imperativeEndings[0][number === "singular" ? 0 : 1];
return aspect === "imperfective"
? ends.map(e => accentPsSyllable(e))
? ends.map((e) => accentPsSyllable(e))
: ends;
}
const [row, col] = getVerbBlockPosFromPerson(person);
return tenseC === "past" ? {
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[] {
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" }]
@ -263,18 +310,27 @@ function ensure3rdPast(rs: T.PsString[], ending: T.PsString[], verb: T.VerbEntry
if (isTlulVerb(verb)) {
// should be imperfective at this point
// the perfective غی should already be covered in the function this is coming from
return [{
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));
const aTip = aspect === "imperfective"
? accentOnNFromEnd(tip, 0)
: tip;
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
@ -286,18 +342,17 @@ function ensure3rdPast(rs: T.PsString[], ending: T.PsString[], verb: T.VerbEntry
// ] : [],
// ];
}
const endsInAwul = (
(["awul", "awúl"].includes(removeFVarients(verb.entry.f).slice(-4)))
&&
(verb.entry.p.slice(-2) === "ول")
);
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 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 ورسېد
@ -305,6 +360,8 @@ function ensure3rdPast(rs: T.PsString[], ending: T.PsString[], verb: T.VerbEntry
return verbEndingConcat(rs, ends);
}
function removeAbility(tense: T.VerbTense | T.AbilityTense | T.ImperativeTense): T.VerbTense | T.ImperativeTense {
function removeAbility(
tense: T.VerbTense | T.AbilityTense | T.ImperativeTense
): T.VerbTense | T.ImperativeTense {
return tense.replace("Modal", "") as T.VerbTense | T.ImperativeTense;
}

File diff suppressed because it is too large Load Diff

View File

@ -6,20 +6,70 @@
*
*/
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 = {
@ -28,23 +78,30 @@ const shwulVB: T.VBBasic = {
long: [{ p: "شول", f: "shwul" }],
short: [{ p: "شو", f: "shw" }],
},
}
};
const shVB: T.VBBasic = {
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",
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,
},
gender: T.Gender;
number: T.NounNumber;
};
}): T.RootsStemsOutput {
const v = removeFVarientsFromVerb(verb);
if (type === "ability") {
@ -63,22 +120,23 @@ function getAbilityRs(
aspect: T.Aspect,
rs: "root" | "stem",
voice: T.Voice,
genderNum: T.GenderNumber,
genderNum: T.GenderNumber
): [[] | [T.VHead], [T.VB, T.VBA]] {
const losesAspect = isTlulVerb(verb) || (isStatComp(verb) && vTransitivity(verb) === "intransitive");
const [vhead, [basicroot]] = voice === "passive"
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,
],
];
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 {
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 });
@ -86,11 +144,10 @@ export function getPastParticiple(verb: T.VerbEntry, voice: T.Voice, { gender, n
if (isStatComp(v) && v.complement) {
return weld(
makeComplement(v.complement, { gender, number }),
getPastParticiple(
statVerb[vTransitivity(verb)],
voice,
{ gender, number },
) as T.VBGenNum,
getPastParticiple(statVerb[vTransitivity(verb)], voice, {
gender,
number,
}) as T.VBGenNum
);
}
if (verb.entry.pprtp && verb.entry.pprtf) {
@ -102,7 +159,11 @@ export function getPastParticiple(verb: T.VerbEntry, voice: T.Voice, { gender, n
number,
};
}
const basicRoot = getRoot(removeFVarientsFromVerb(verb), { gender, number }, "imperfective")[1][0];
const basicRoot = getRoot(
removeFVarientsFromVerb(verb),
{ gender, number },
"imperfective"
)[1][0];
const longRoot = getLongVB(basicRoot);
const rootWLengths = possiblePPartLengths(longRoot);
/* istanbul ignore next */
@ -116,7 +177,9 @@ export function getPastParticiple(verb: T.VerbEntry, voice: T.Voice, { gender, n
number,
};
function addTail(ps: T.SingleOrLengthOpts<T.PsString[]>): T.SingleOrLengthOpts<T.PsString[]> {
function addTail(
ps: T.SingleOrLengthOpts<T.PsString[]>
): T.SingleOrLengthOpts<T.PsString[]> {
return fmapSingleOrLengthOpts((x) => {
const withTail = concatPsString(x[0], { p: "ی", f: "ey" });
return inflectPattern3(withTail, { gender, number });
@ -124,57 +187,100 @@ export function getPastParticiple(verb: T.VerbEntry, voice: T.Voice, { gender, n
}
}
function getPassivePp(verb: T.VerbEntryNoFVars, genderNumber: T.GenderNumber): T.WeldedGN {
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),
getPassivePp(statVerb.transitive, genderNumber)
);
}
const basicRoot = getRoot(verb, genderNumber, isKawulVerb(verb) ? "perfective" : "imperfective")[1][0];
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;
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]] {
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)],
];
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))) {
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,
aspect
)[1][0] as T.VBBasic;
const complement = makeComplement(verb.complement, genderNum);
return aspect === "perfective"
? [[complement], [auxStem]]
: [[], [weld(complement, auxStem)]];
}
const base = aspect === "imperfective"
const base =
aspect === "imperfective"
? accentOnNFromEnd(makePsString(verb.entry.p, verb.entry.f), 0)
: removeAccents(
(verb.entry.prp && verb.entry.prf)
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];
const [perfectiveHead, rest] =
aspect === "perfective" ? getPerfectiveHead(base, verb) : [undefined, base];
if (verb.entry.f === "tlul" && aspect === "perfective") {
return [
[{ type: "PH", ps: { p: "لا", f: "láa" } }],
[
{
type: "VB",
ps: {
long: [{ p: "ړل", f: "Rul" }],
short: [{ p: "ړ", f: "R" }],
},
},
],
];
}
return [
perfectiveHead ? [perfectiveHead] : [],
[
{
type: "VB",
ps: aspect === "imperfective"
ps:
aspect === "imperfective"
? {
long: [rest],
short: [addTrailingAccent(removeL(rest))],
@ -182,22 +288,32 @@ function getRoot(verb: T.VerbEntryNoFVars, genderNum: T.GenderNumber, aspect: T.
: {
long: [rest],
short: [removeL(rest)],
...(aspect === "perfective" && isKawulVerb(verb)) ? {
...(aspect === "perfective" && isKawulVerb(verb)
? {
mini: [{ p: "ک", f: "k" }],
} : {},
}
: {}),
},
},
],
];
}
function getStem(verb: T.VerbEntryNoFVars, genderNum: T.GenderNumber, aspect: T.Aspect): [[T.VHead] | [], [T.VB]] {
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))) {
if (
verb.complement &&
statComp &&
(aspect === "perfective" || statCompImperfectiveSpace(verb))
) {
const auxStem = getStem(
statVerb[vTransitivity(verb)],
genderNum,
aspect,
aspect
)[1][0] as T.VBBasic;
const complement = makeComplement(verb.complement, genderNum);
return aspect === "perfective"
@ -208,17 +324,22 @@ function getStem(verb: T.VerbEntryNoFVars, genderNum: T.GenderNumber, aspect: T.
if (verb.entry.f === "tlul") {
return tlulPerfectiveStem(genderNum);
}
if (!isKedul(verb) && vTransitivity(verb) === "intransitive" && verb.entry.p.endsWith("ېدل")) {
return splitEdulIntans(edulIntransBase(verb))
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 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] : [],
@ -231,11 +352,14 @@ function getStem(verb: T.VerbEntryNoFVars, genderNum: T.GenderNumber, aspect: T.
];
}
const rawBase = removeL(makePsString(verb.entry.p, verb.entry.f));
const base = verb.entry.psp && verb.entry.psf
const base =
verb.entry.psp && verb.entry.psf
? [makePsString(verb.entry.psp, verb.entry.psf)]
: (vTransitivity(verb) === "intransitive" && rawBase.p.endsWith("ېد"))
: vTransitivity(verb) === "intransitive" && rawBase.p.endsWith("ېد")
? edulIntransBase(verb)
: isKawulVerb(verb) || statComp || (countSyllables(rawBase) > 1 && rawBase.f.endsWith("aw"))
: isKawulVerb(verb) ||
statComp ||
(countSyllables(rawBase) > 1 && rawBase.f.endsWith("aw"))
? [addTrailingAccent(rawBase)]
: [rawBase];
return [
@ -247,13 +371,15 @@ function getStem(verb: T.VerbEntryNoFVars, genderNum: T.GenderNumber, aspect: T.
},
],
];
function splitEdulIntans(ps: T.SingleOrLengthOpts<T.PsString[]>): [[T.PH] | [], [T.VB]] {
const [ph, long] = ("long" in ps)
function splitEdulIntans(
ps: T.SingleOrLengthOpts<T.PsString[]>
): [[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;
: getPerfectiveHead(ps[0], verb);
const short =
"long" in ps ? getPerfectiveHead(ps.short[0], verb) : undefined;
if (short) {
return [
ph ? [ph] : [],
@ -268,27 +394,28 @@ function getStem(verb: T.VerbEntryNoFVars, genderNum: T.GenderNumber, aspect: T.
],
];
}
return [
ph ? [ph] : [],
[
{ type: "VB", ps: [long] },
],
];
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] {
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({
ps: accentOnNFromEnd(
{
p: base.p.slice(0, v.entry.separationAtP),
f: base.f.slice(0, v.entry.separationAtF),
}, 0),
},
0
),
};
const rest = {
p: base.p.slice(v.entry.separationAtP),
@ -299,20 +426,11 @@ function getPerfectiveHead(base: T.PsString, v: T.VerbEntryNoFVars): [T.PH, T.Ps
const [ph, rest]: [T.PH | undefined, T.PsString] = v.entry.noOo
? [undefined, base]
: v.entry.sepOo
? [
{ type: "PH", ps: { p: "و ", f: "óo`"}},
base,
]
? [{ 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),
]
? [{ type: "PH", ps: { p: "وا", f: "wáa" } }, removeAStart(base)]
: ["óo", "oo"].includes(base.f.slice(0, 2))
? [
{ type: "PH", ps: { p: "و", f: "wÚ" }},
base,
]
? [{ 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" } },
@ -320,25 +438,19 @@ function getPerfectiveHead(base: T.PsString, v: T.VerbEntryNoFVars): [T.PH, T.Ps
p: base.p.slice(2),
f: base.f.slice(2),
},
] : ["é", "e"].includes(base.f.slice(0, 2)) && base.p.slice(0, 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),
];
]
: ["ó", "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),
@ -347,13 +459,12 @@ function getPerfectiveHead(base: T.PsString, v: T.VerbEntryNoFVars): [T.PH, T.Ps
}
}
function edulIntransBase(v: T.VerbEntryNoFVars): T.SingleOrLengthOpts<T.PsString[]> {
function edulIntransBase(
v: T.VerbEntryNoFVars
): T.SingleOrLengthOpts<T.PsString[]> {
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)
const long: T.PsString[] = [concatPsString(base, { p: "ېږ", f: "éG" })];
const short: T.PsString[] | undefined = v.entry.shortIntrans
? [base]
: undefined;
return short ? { short, long } : long;

View File

@ -1,13 +1,12 @@
import * as T from "../../../types";
import {
capitalizeFirstLetter,
concatPsString, getLong,
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";
@ -22,18 +21,15 @@ import {
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<T.PsString[]> {
@ -61,9 +57,20 @@ type BlankoutOptions = {
// }));
// }
export function compileEP(EP: T.EPRendered): { ps: T.SingleOrLengthOpts<T.PsString[]>, 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<T.PsString[]>, e?: string[] } {
export function compileEP(EP: T.EPRendered): {
ps: T.SingleOrLengthOpts<T.PsString[]>;
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<T.PsString[]>; e?: string[] } {
const psResult = compileEPPs(EP.blocks, EP.kids, EP.omitSubject, blankOut);
return {
ps: combineLengths ? flattenLengths(psResult) : psResult,
@ -71,81 +78,143 @@ export function compileEP(EP: T.EPRendered, combineLengths?: boolean, blankOut?:
};
}
export function compileVP(VP: T.VPRendered, form: T.FormVersion): { ps: T.SingleOrLengthOpts<T.PsString[]>, 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<T.PsString[]>, e?: string [] } {
export function compileVP(
VP: T.VPRendered,
form: T.FormVersion
): { ps: T.SingleOrLengthOpts<T.PsString[]>; 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<T.PsString[]>; 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),
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;
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));
return removeDuplicates(
combineIntoText(blocksWKids, subjectPerson, blankOut)
);
}
function compileEPPs(blocks: T.Block[][], kids: T.Kid[], omitSubject: boolean, blankOut?: BlankoutOptions): T.SingleOrLengthOpts<T.PsString[]> {
function compileEPPs(
blocks: T.Block[][],
kids: T.Kid[],
omitSubject: boolean,
blankOut?: BlankoutOptions
): T.SingleOrLengthOpts<T.PsString[]> {
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[],
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 subjectPerson =
getSubjectSelectionFromBlocks(blocks).selection.selection.person;
const blocksWKids = putKidsInKidsSection(
omitSubject ? blocks.map(blks => blks.filter(b => b.block.type !== "subjectSelection")) : blocks,
omitSubject
? blocks.map((blks) =>
blks.filter((b) => b.block.type !== "subjectSelection")
)
: blocks,
kids,
!!blankOut?.kidsSection
);
return removeDuplicates(combineIntoText(blocksWKids, subjectPerson, blankOut));
return removeDuplicates(
combineIntoText(blocksWKids, subjectPerson, blankOut)
);
}
export function filterForVisibleBlocksVP(blocks: T.Block[][], form: T.FormVersion, king: "subject" | "object"): T.Block[][] {
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) => {
return blocks.map((blks) =>
blks.filter((block) => {
if (form.removeKing) {
if (
(king === "subject" && block.block.type === "subjectSelection")
||
(king === "subject" && block.block.type === "subjectSelection") ||
(king === "object" && block.block.type === "objectSelection")
) return false;
)
return false;
}
if (form.shrinkServant) {
if (
(servant === "subject" && block.block.type === "subjectSelection")
||
(servant === "subject" && block.block.type === "subjectSelection") ||
(servant === "object" && block.block.type === "objectSelection")
) return false;
)
return false;
}
if (block.block.type === "objectSelection" && typeof block.block.selection !== "object") {
if (
block.block.type === "objectSelection" &&
typeof block.block.selection !== "object"
) {
return false;
}
return true;
}));
})
);
}
export function filterForVisibleBlocksEP(blocks: T.Block[][], omitSubject: boolean): T.Block[][] {
export function filterForVisibleBlocksEP(
blocks: T.Block[][],
omitSubject: boolean
): T.Block[][] {
if (!omitSubject) return blocks;
return blocks.map(blks => blks.filter(({ block }) => {
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[] {
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 {
p: x.p.replace(blank.p + blank.p, blank.p),
@ -156,49 +225,76 @@ function combineIntoText(piecesWVars: (T.Block | T.Kid | T.PsString)[][], subjec
const first = pieces[0];
const next = pieces[1];
const rest = pieces.slice(1);
const firstPs = ("p" in first)
// 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?.equative &&
"block" in first &&
first.block.type === "equative") ||
(blankOut?.verb && "block" in first && isRenderedVerbB(first)) ||
(blankOut?.predicate && "block" in first && first.block.type === "predicateSelection")
)
(blankOut?.predicate &&
"block" in first &&
first.block.type === "predicateSelection")
? [blank]
: ((blankOut?.ba) && "kid" in first && first.kid.type === "ba")
: blankOut?.ba && "kid" in first && first.kid.type === "ba"
? [kidsBlank]
: (blankOut?.negative && "block" in first && first.block.type === "negative")
: 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(
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: " " } : "")
!("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,
))
r
)
).map(removeDoubleBlanks);
)
)
.map(removeDoubleBlanks);
}
return piecesWVars.flatMap(combine);
}
function getPsFromPiece(piece: T.Block | T.Kid, subjectPerson: T.Person): T.PsString[] {
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"],
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") {
if (
piece.block.type === "subjectSelection" ||
piece.block.type === "predicateSelection"
) {
return getPashtoFromRendered(piece.block.selection, subjectPerson);
}
if (piece.block.type === "AP") {
@ -222,7 +318,10 @@ function getPsFromPiece(piece: T.Block | T.Kid, subjectPerson: T.Person): T.PsSt
if (piece.block.type === "complement") {
if (piece.block.selection.type === "sandwich") {
// TODO: Kinda cheating
return getPashtoFromRendered({ type: "AP", selection: piece.block.selection }, false);
return getPashtoFromRendered(
{ type: "AP", selection: piece.block.selection },
false
);
}
return piece.block.selection.ps;
}
@ -241,7 +340,9 @@ function getPsFromPiece(piece: T.Block | T.Kid, subjectPerson: T.Person): T.PsSt
}
function getPsFromWelded(v: T.Welded): T.PsString[] {
function getPsFromSide(v: T.VBBasic | T.Welded | T.NComp | T.VBGenNum): T.PsString[] {
function getPsFromSide(
v: T.VBBasic | T.Welded | T.NComp | T.VBGenNum
): T.PsString[] {
if (v.type === "VB") {
return flattenLengths(v.ps);
}
@ -252,11 +353,9 @@ function getPsFromWelded(v: T.Welded): T.PsString[] {
}
const left = getPsFromSide(v.left);
const right = getPsFromSide(v.right);
return left.flatMap(leftVar => (
right.flatMap(rightVar => (
concatPsString(leftVar, " ", rightVar)
))
));
return left.flatMap((leftVar) =>
right.flatMap((rightVar) => concatPsString(leftVar, " ", rightVar))
);
}
function getEngAPs(blocks: T.Block[][]): string {
@ -279,13 +378,17 @@ function getEngComplement(blocks: T.Block[][]): string | undefined {
return comp.selection.e;
}
function putKidsInKidsSection(blocksWVars: T.Block[][], kids: T.Kid[], enforceKidsSectionBlankout: boolean): (T.Block | T.Kid | T.PsString)[][] {
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,
...(enforceKidsSectionBlankout ? [kidsBlank] : kids),
...rest,
];
}
@ -293,40 +396,65 @@ function putKidsInKidsSection(blocksWVars: T.Block[][], kids: T.Kid[], enforceKi
}
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 || "")
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 /* && isStativeHelper(getVerbFromBlocks(VP.blocks).block.verb))*/
? ` ${complement}`
: "")
+ APs;
: "") +
APs
);
}
const engSubj = getSubjectSelectionFromBlocks(VP.blocks).selection;
const obj = getObjectSelectionFromBlocks(VP.blocks).selection;
const engObj = typeof obj === "object"
const engObj =
typeof obj === "object"
? obj
: (obj === "none" || obj === T.Person.ThirdPlurMale)
: 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, {
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)
})
)
.map(capitalizeFirstLetter)
: undefined;
}
function compileEnglishEP(EP: T.EPRendered): string[] | undefined {
function insertEWords(e: string, { subject, predicate, APs }: { subject: string, predicate: string, APs: string }): string {
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;
@ -335,21 +463,26 @@ function compileEnglishEP(EP: T.EPRendered): string[] | undefined {
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, {
const b =
EP.englishBase && engSubj && engPred
? EP.englishBase.map((e) =>
insertEWords(e, {
subject: engSubj,
predicate: engPred,
APs: engAPs,
}))
})
)
: undefined;
return b?.map(capitalizeFirstLetter);
}
export function checkForMiniPronounsError(s: T.EPSelectionState | T.VPSelectionState): undefined | string {
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
));
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];
}
@ -358,13 +491,15 @@ export function checkForMiniPronounsError(s: T.EPSelectionState | T.VPSelectionS
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[];
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";
}
@ -375,7 +510,9 @@ export function checkForMiniPronounsError(s: T.EPSelectionState | T.VPSelectionS
return undefined;
}
function findPossesivesInNP(NP: T.Rendered<T.NPSelection> | T.ObjectNP | undefined): T.Rendered<T.NPSelection>[] {
function findPossesivesInNP(
NP: T.Rendered<T.NPSelection> | T.ObjectNP | undefined
): T.Rendered<T.NPSelection>[] {
if (NP === undefined) return [];
if (typeof NP !== "object") return [];
if (!NP.selection.possesor) return [];
@ -392,26 +529,34 @@ function findPossesivesInNP(NP: T.Rendered<T.NPSelection> | T.ObjectNP | undefin
return findPossesivesInNP(NP.selection.possesor.np);
}
function findPossesivesInAdjectives(a: T.Rendered<T.AdjectiveSelection>[]): T.Rendered<T.NPSelection>[] {
return a.reduce((accum, curr): T.Rendered<T.NPSelection>[] => ([
function findPossesivesInAdjectives(
a: T.Rendered<T.AdjectiveSelection>[]
): T.Rendered<T.NPSelection>[] {
return a.reduce(
(accum, curr): T.Rendered<T.NPSelection>[] => [
...accum,
...findPossesivesInAdjective(curr),
]), [] as T.Rendered<T.NPSelection>[])
],
[] as T.Rendered<T.NPSelection>[]
);
}
function findPossesivesInAdjective(a: T.Rendered<T.AdjectiveSelection>): T.Rendered<T.NPSelection>[] {
function findPossesivesInAdjective(
a: T.Rendered<T.AdjectiveSelection>
): T.Rendered<T.NPSelection>[] {
if (!a.sandwich) return [];
return findPossesivesInNP(a.sandwich.inside);
}
export function flattenLengths(r: T.SingleOrLengthOpts<T.PsString[] | T.PsString>): T.PsString[] {
export function flattenLengths(
r: T.SingleOrLengthOpts<T.PsString[]>
): T.PsString[] {
if ("long" in r) {
return Object.values(r).flat();
// 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];
}
return [...r.long, ...r.short, ...("mini" in r && r.mini ? r.mini : [])];
}
if (Array.isArray(r)) {
return r;
}
return [r];
}

View File

@ -1,24 +1,24 @@
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;
@ -27,17 +27,16 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
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)
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 inflectSubject =
isPast && isTransitive && !isMascSingAnimatePattern4(subject);
const inflectObject = !isPast && isFirstOrSecondPersPronoun(object);
// Render Elements
const firstBlocks = renderVPBlocks(VP.blocks, VP.externalComplement, {
@ -49,12 +48,16 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
const { vbs, hasBa } = renderVerb({
verb: VP.verb.verb,
tense: VP.verb.tense,
person: kingPerson,
complementGenNum: personToGenNum(objectPerson || kingPerson),
subject: subjectPerson,
object: objectPerson,
voice: VP.verb.voice,
negative: VP.verb.negative,
});
const VBwNeg = insertNegative(vbs, VP.verb.negative, isImperativeTense(VP.verb.tense));
const VBwNeg = insertNegative(
vbs,
VP.verb.negative,
isImperativeTense(VP.verb.tense)
);
// just enter the negative in the verb blocks
return {
type: "VPRendered",
@ -63,7 +66,7 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
isPast,
isTransitive,
isCompound: VP.verb.isCompound,
blocks: VBwNeg.map(VBvars => [...firstBlocks, ...VBvars]),
blocks: VBwNeg.map((VBvars) => [...firstBlocks, ...VBvars]),
kids: getVPKids(hasBa, VP.blocks, VP.form, king),
englishBase: renderEnglishVPBase({
subjectPerson,
@ -75,23 +78,35 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
};
}
function getVPKids(hasBa: boolean, blocks: T.VPSBlockComplete[], form: T.FormVersion, king: "subject" | "object"): T.Kid[] {
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)
const shrunkenServant =
form.shrinkServant && servantNP
? makeKid(shrinkServant(servantNP))
: undefined;
const shrunkenPossesives = findPossesivesToShrink(removeAbbreviated(blocks, form, king)).map(makeKid);
const shrunkenPossesives = findPossesivesToShrink(
removeAbbreviated(blocks, form, king)
).map(makeKid);
return orderKids([
...hasBa ? [makeKid({ type: "ba" })] : [],
...shrunkenServant ? [shrunkenServant] : [],
...shrunkenPossesives ? shrunkenPossesives : [],
...(hasBa ? [makeKid({ type: "ba" })] : []),
...(shrunkenServant ? [shrunkenServant] : []),
...(shrunkenPossesives ? shrunkenPossesives : []),
]);
}
function removeAbbreviated(blocks: T.VPSBlockComplete[], form: T.FormVersion, king: "subject" | "object"): T.VPSBlockComplete[] {
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;
@ -105,12 +120,18 @@ function removeAbbreviated(blocks: T.VPSBlockComplete[], form: T.FormVersion, ki
});
}
function insertNegative(blocks: T.VerbRenderedOutput, negative: boolean, imperative: boolean): T.Block[][] {
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 blocksNoAccentA = mapVerbRenderedOutput(removeAccents, blocks)
.flat()
.map(makeBlock);
const neg = makeBlock({ type: "negative", imperative });
const nonStandPerfectiveSplit = hasNonStandardPerfectiveSplit(blocks);
if (blocks[1].length === 2) {
@ -120,7 +141,7 @@ function insertNegative(blocks: T.VerbRenderedOutput, negative: boolean, imperat
insertFromEnd(swapEndingBlocks(blocksA), neg, 2),
insertFromEnd(swapEndingBlocks(blocksA, 2), neg, 3),
insertFromEnd(blocksNoAccentA, neg, 1),
]
];
}
return [
insertFromEnd(swapEndingBlocks(blocksA), neg, 2),
@ -149,11 +170,7 @@ function insertFromEnd<X>(arr: X[], x: X, n: number): X[] {
if (n === 0) {
return [...arr, x];
}
return [
...arr.slice(0, arr.length - n),
x,
...arr.slice(-n),
];
return [...arr.slice(0, arr.length - n), x, ...arr.slice(-n)];
}
function hasNonStandardPerfectiveSplit([[ph]]: T.VerbRenderedOutput): boolean {
@ -177,26 +194,39 @@ function shrinkServant(np: T.NPSelection): T.MiniPronoun {
};
}
function renderVPBlocks(blocks: T.VPSBlockComplete[], externalComplement: T.VPSelectionComplete["externalComplement"], config: {
inflectSubject: boolean,
inflectObject: boolean,
king: "subject" | "object",
complementPerson: T.Person | undefined,
}): T.Block[] {
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"
const adverbPerson =
typeof object.selection === "object"
? getPersonFromNP(object.selection)
: getPersonFromNP(subject.selection);
const b = externalComplement ? [...blocks, { block: externalComplement }] : blocks
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),
selection: renderNPSelection(
block.selection,
config.inflectSubject,
false,
"subject",
config.king === "subject" ? "king" : "servant",
false
),
}),
];
}
@ -211,7 +241,14 @@ function renderVPBlocks(blocks: T.VPSBlockComplete[], externalComplement: T.VPSe
}),
];
}
const selection = renderNPSelection(object, config.inflectObject, true, "object", config.king === "object" ? "king" : "servant", false);
const selection = renderNPSelection(
object,
config.inflectObject,
true,
"object",
config.king === "object" ? "king" : "servant",
false
);
return [
...blocks,
makeBlock({
@ -232,43 +269,58 @@ function renderVPBlocks(blocks: T.VPSBlockComplete[], externalComplement: T.VPSe
renderComplementSelection(
block,
// just for typesafety // TODO: only include the person if we're doing an adjective
config.complementPerson || T.Person.FirstSingMale,
)),
config.complementPerson || T.Person.FirstSingMale
)
),
];
}, [] as T.Block[]);
}
function whatsAdjustable(VP: T.VPSelectionComplete): "both" | "king" | "servant" {
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")
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.voice === "active"
? "both"
: "king"
: VP.verb.transitivity === "intransitive"
? "king"
// grammTrans
: isPastTense(VP.verb.tense)
: // 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 } {
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 ? {
return isPast
? {
king: "object",
servant: "subject",
} : {
}
: {
king: "subject",
servant: "object",
};
}
function isFirstOrSecondPersPronoun(o: "none" | T.NPSelection | T.Person.ThirdPlurMale): boolean {
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);
@ -278,8 +330,10 @@ 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");
return (
isPattern4Entry(np.selection.entry) &&
np.selection.entry.c.includes("anim.") &&
np.selection.number === "singular" &&
np.selection.gender === "masc"
);
}

View File

@ -1,13 +1,15 @@
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 = [
@ -23,116 +25,120 @@ export function isInvalidSubjObjCombo(subj: T.Person, obj: T.Person): boolean {
T.Person.SecondPlurFemale,
];
return (
(firstPeople.includes(subj) && firstPeople.includes(obj))
||
(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 {
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;
}
@ -144,15 +150,21 @@ export function getPersonFromNP(np: T.NPSelection | T.ObjectNP): T.Person | unde
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);
? 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, " "));
}
export function getTenseFromVerbSelection(vs: T.VerbSelection): T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense {
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";
@ -208,25 +220,29 @@ export function perfectTenseHasBa(tense: T.PerfectTense): boolean {
}
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<V extends T.VPSelectionState | T.VPSelectionComplete>(vps: V): V {
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")) {
if (
!subject ||
!(typeof object === "object") ||
vps.verb.tenseCategory === "imperative"
) {
return vps;
}
return {
...vps,
blocks: adjustObjectSelection(
adjustSubjectSelection(vps.blocks, object),
subject,
subject
),
};
}
@ -237,12 +253,14 @@ export function switchSubjObj<V extends T.VPSelectionState | T.VPSelectionComple
...vps,
blocks: adjustObjectSelection(
adjustSubjectSelection(vps.blocks, object),
subject,
subject
),
};
}
export function completeVPSelection(vps: T.VPSelectionState): T.VPSelectionComplete | undefined {
export function completeVPSelection(
vps: T.VPSelectionState
): T.VPSelectionComplete | undefined {
if (!VPSBlocksAreComplete(vps.blocks)) {
return undefined;
}
@ -267,13 +285,18 @@ export function isThirdPerson(p: T.Person): boolean {
);
}
export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState): T.VPSelectionState {
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 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: {
@ -311,7 +334,7 @@ export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState):
if (typeof object !== "object" || object.selection.type !== "pronoun") {
return {
...vps,
blocks: adjustSubjectSelection(vps.blocks, default2ndPersSubject)
blocks: adjustSubjectSelection(vps.blocks, default2ndPersSubject),
};
}
return {
@ -324,7 +347,7 @@ export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState):
...object.selection,
person: getNon2ndPersPronoun(),
},
},
}
),
};
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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"