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": { "dependencies": {
"fp-ts": "^2.16.0",
"react-media-hook": "^0.5.0",
"react-select": "^5.4.0" "react-select": "^5.4.0"
} }
} }

View File

@ -27,24 +27,37 @@ const persInfs: {
{ {
label: "fem. plur", label: "fem. plur",
value: "femPlur", value: "femPlur",
} },
]; ];
function PersInfsPicker(props: { function PersInfsPicker(props: {
transitivity: T.Transitivity, subjOrObj: "subject" | "object";
persInf: T.PersonInflectionsField, persInf: T.PersonInflectionsField;
handleChange: (persInf: T.PersonInflectionsField) => void, handleChange: (persInf: T.PersonInflectionsField) => void;
}) { }) {
function hChange(e: any) { function hChange(e: any) {
const newValue = e.target.value as T.PersonInflectionsField; const newValue = e.target.value as T.PersonInflectionsField;
props.handleChange(newValue); 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"> <div className="mr-2">
When the <strong>{props.transitivity === "intransitive" ? "subject" : "object"}</strong> is When the <strong>{props.subjOrObj}</strong> is
</div> </div>
<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) => ( {persInfs.map((pers) => (
<option key={pers.value} value={pers.value}> <option key={pers.value} value={pers.value}>
{pers.label} {pers.label}
@ -52,7 +65,8 @@ function PersInfsPicker(props: {
))} ))}
</select> </select>
</div> </div>
</div>; </div>
);
} }
export default PersInfsPicker; export default PersInfsPicker;

View File

@ -3,15 +3,32 @@ import * as T from "../../types";
import Pashto from "./Pashto"; import Pashto from "./Pashto";
import Phonetics from "./Phonetics"; 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"> const arrowDown = (
<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
</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 }: { function TableCell({
item: T.ArrayOneOrMore<T.PsString>, item,
textOptions: T.TextOptions, textOptions,
center?: boolean, center,
noBorder?: boolean, noBorder,
}: {
item: T.PsString[];
textOptions: T.TextOptions;
center?: boolean;
noBorder?: boolean;
}) { }) {
const [version, setVersion] = useState(0); const [version, setVersion] = useState(0);
useEffect(() => setVersion(0), [item]); useEffect(() => setVersion(0), [item]);
@ -20,14 +37,16 @@ function TableCell({ item, textOptions, center, noBorder }: {
} }
const w = item[version] || item[0]; const w = item[version] || item[0];
return ( return (
<td style={{ ...noBorder ? { border: "none" } : {} }}> <td style={{ ...(noBorder ? { border: "none" } : {}) }}>
<div style={{ <div
style={{
display: "flex", display: "flex",
flexDirection: "row", flexDirection: "row",
flexWrap: "wrap", flexWrap: "wrap",
justifyContent: center ? "center" : "space-between", justifyContent: center ? "center" : "space-between",
alignItems: "center", alignItems: "center",
}}> }}
>
<div> <div>
<div> <div>
<Pashto opts={textOptions}>{w}</Pashto> <Pashto opts={textOptions}>{w}</Pashto>
@ -35,15 +54,25 @@ function TableCell({ item, textOptions, center, noBorder }: {
<div> <div>
<Phonetics opts={textOptions}>{w}</Phonetics> <Phonetics opts={textOptions}>{w}</Phonetics>
</div> </div>
{w.e && (Array.isArray(w.e) {w.e &&
? w.e.map(e => <div key={e} className="text-muted small">{e}</div>) (Array.isArray(w.e) ? (
: <div className="text-muted">{w.e}</div>)} w.e.map((e) => (
<div key={e} className="text-muted small">
{e}
</div> </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} ver. {version + 1}/{item.length} {arrowDown}
</button> </button>
} )}
</div> </div>
</td> </td>
); );

View File

@ -16,46 +16,67 @@ import {
getEnglishPersonInfo, getEnglishPersonInfo,
isSentenceForm, isSentenceForm,
} from "../../lib/src/misc-helpers"; } from "../../lib/src/misc-helpers";
import { import { isAllOne } from "../../lib/src/p-text-helpers";
isAllOne,
} from "../../lib/src/p-text-helpers";
import * as T from "../../types"; 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) { if (!displayForm.past) {
return null; return null;
} }
const beginning = "Verb agrees with the "; 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" ? "object"
: "subject"; : "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." ? " which is an unwritten 3rd pers. masc. object."
: (info.type === "generative stative compound" || info.type === "dynamic compound") : info.type === "generative stative compound" ||
? ` which is the complement ${info.objComplement.plural ? info.objComplement.plural.p : info.objComplement.entry.p} (${getEnglishPersonInfo(info.objComplement.person)})` info.type === "dynamic compound"
: "" ? ` which is the complement ${
return <><strong>Note:</strong> {beginning}<strong>{agreesWith}</strong>{extraExplanation}</> 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 }: { function VerbFormDisplay({
displayForm: T.DisplayForm | T.VerbForm | T.ImperativeForm, displayForm,
english?: T.EnglishBlock | string, textOptions,
textOptions: T.TextOptions, info,
showingFormInfo: boolean, showingFormInfo,
info?: T.NonComboVerbInfo, english,
shortDefault?: boolean, 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 defaultLength = shortDefault ? "short" : "long";
const [persInf, setPersInf] = useState<T.PersonInflectionsField>("mascSing"); const [persInf, setPersInf] = useState<T.PersonInflectionsField>("mascSing");
const [length, setLength] = useState<T.Length>(defaultLength); const [length, setLength] = useState<T.Length>(defaultLength);
const [showingExplanation, setShowingExplanation] = useState<boolean>(false); const [showingExplanation, setShowingExplanation] = useState<boolean>(false);
const block = "label" in displayForm ? displayForm.form : displayForm; const block = "label" in displayForm ? displayForm.form : displayForm;
const chosenPersInf = "mascSing" in block const chosenPersInf = "mascSing" in block ? block[persInf] : block;
? block[persInf] const form =
: block; "long" in chosenPersInf
const form = "long" in chosenPersInf
? chosenPersInf[length] || chosenPersInf.short ? chosenPersInf[length] || chosenPersInf.short
: chosenPersInf; : chosenPersInf;
useEffect(() => { useEffect(() => {
@ -69,51 +90,80 @@ function VerbFormDisplay({ displayForm, textOptions, info, showingFormInfo, engl
useEffect(() => { useEffect(() => {
setShowingExplanation(false); setShowingExplanation(false);
}, [block]); }, [block]);
const hasVariations = (!("masc" in form)) && (!("p" in form)) && (!isSentenceForm(form)) && !isAllOne(form as T.VerbBlock | T.ImperativeBlock); const hasVariations =
return <> !("masc" in form) &&
{(("label" in displayForm && info) && showingFormInfo) && <> !("p" in form) &&
{(hasVariations || displayForm.past) && <p className="small text-muted"> !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)} {agreementInfo(info, displayForm)}
</p>} </p>
)}
<div className="mb-1 d-flex justify-content-between align-items-center"> <div className="mb-1 d-flex justify-content-between align-items-center">
<samp>Formula: {displayForm.formula}</samp> <samp>Formula: {displayForm.formula}</samp>
<button className="btn btn-sm btn-outline-secondary text-nowrap ml-2" onClick={() => setShowingExplanation(!showingExplanation)}> <button
<i className={`fas fa-caret-${showingExplanation ? "down" : "right"}`} /> Meaning className="btn btn-sm btn-outline-secondary text-nowrap ml-2"
onClick={() => setShowingExplanation(!showingExplanation)}
>
<i
className={`fas fa-caret-${
showingExplanation ? "down" : "right"
}`}
/>{" "}
Meaning
</button> </button>
</div> </div>
{showingExplanation && <div className="my-2"> {showingExplanation && (
{displayForm.explanation} <div className="my-2">{displayForm.explanation}</div>
</div>} )}
</>} </>
{"long" in chosenPersInf && )}
{"long" in chosenPersInf && (
<div className="text-center"> <div className="text-center">
<ButtonSelect <ButtonSelect
small small
options={[ options={[
{ label: "Long", value: "long" }, { label: "Long", value: "long" },
{ label: "Short", value: "short" }, { label: "Short", value: "short" },
..."mini" in chosenPersInf ? [{ ...("mini" in chosenPersInf
label: "Mini", value: "mini", ? [
}] : [], {
label: "Mini",
value: "mini",
},
]
: []),
]} ]}
value={length} value={length}
handleChange={(p) => setLength(p as T.Length)} handleChange={(p) => setLength(p as T.Length)}
/> />
</div> </div>
} )}
{("mascSing" in block && info) && <PersonInfsPicker {"mascSing" in block && info && (
<PersonInfsPicker
persInf={persInf} persInf={persInf}
handleChange={(p) => setPersInf(p)} handleChange={(p) => setPersInf(p)}
transitivity={info.transitivity} subjOrObj={info.transitivity === "transitive" ? "object" : "subject"}
/>} />
{"masc" in form ? )}
{"masc" in form ? (
<InflectionsTable inf={form} textOptions={textOptions} /> <InflectionsTable inf={form} textOptions={textOptions} />
: "p" in form ? ) : "p" in form ? (
<SingleItemDisplay item={form} english={english} textOptions={textOptions} /> <SingleItemDisplay
: item={form}
english={english}
textOptions={textOptions}
/>
) : (
<VerbTable block={form} english={english} textOptions={textOptions} /> <VerbTable block={form} english={english} textOptions={textOptions} />
} )}
</> </>
);
} }
export default VerbFormDisplay; export default VerbFormDisplay;

View File

@ -6,10 +6,7 @@
* *
*/ */
import { import { CSSProperties, useState } from "react";
CSSProperties,
useState,
} from "react";
import { import {
pickPersInf, pickPersInf,
hasPersInfs, hasPersInfs,
@ -39,34 +36,52 @@ const title: CSSProperties = {
marginTop: "0.5rem", marginTop: "0.5rem",
}; };
export function RootsAndStems({ textOptions, info, hidePastParticiple, highlighted, noTails }: { export function RootsAndStems({
textOptions: T.TextOptions, textOptions,
info: T.NonComboVerbInfo | T.PassiveRootsAndStems | T.AbilityRootsAndStems, info,
hidePastParticiple?: boolean, hidePastParticiple,
highlighted?: T.RootsOrStemsToHighlight, highlighted,
noTails?: boolean, 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) const hasPerfectiveSplit =
&& !!(info.root.perfectiveSplit || info.stem.perfectiveSplit); "perfectiveSplit" in info.root &&
"perfectiveSplit" in info.stem &&
!!(info.root.perfectiveSplit || info.stem.perfectiveSplit);
const showPersInf = hasPersInfs(info); const showPersInf = hasPersInfs(info);
const [persInf, setPersInf] = useState<T.PersonInflectionsField>("mascSing"); const [persInf, setPersInf] = useState<T.PersonInflectionsField>("mascSing");
const [split, setSplit] = useState<boolean>(false); const [split, setSplit] = useState<boolean>(false);
const perfectiveStem = (info.stem.perfectiveSplit && split) const perfectiveStem =
info.stem.perfectiveSplit && split
? info.stem.perfectiveSplit ? info.stem.perfectiveSplit
: info.stem.perfective; : info.stem.perfective;
const perfectiveRoot = (info.root.perfectiveSplit && split) const perfectiveRoot =
info.root.perfectiveSplit && split
? info.root.perfectiveSplit ? info.root.perfectiveSplit
: info.root.perfective; : info.root.perfective;
const colClass = "col col-md-5 px-0 mb-1"; const colClass = "col col-md-5 px-0 mb-1";
const rowClass = "row justify-content-between"; const rowClass = "row justify-content-between";
return ( return (
<div className="mt-3"> <div className="mt-3">
{showPersInf && <PersonInfsPicker {showPersInf && (
<PersonInfsPicker
persInf={persInf} persInf={persInf}
handleChange={(p) => setPersInf(p)} handleChange={(p) => setPersInf(p)}
transitivity={"transitivity" in info ? info.transitivity : "intransitive"} subjOrObj={
/>} "transitivity" in info && info.transitivity === "intransitive"
<div className="verb-info" style={{ ? "subject"
: "object"
}
/>
)}
<div
className="verb-info"
style={{
textAlign: "center", textAlign: "center",
maxWidth: "500px", maxWidth: "500px",
margin: "0 auto", margin: "0 auto",
@ -74,7 +89,8 @@ export function RootsAndStems({ textOptions, info, hidePastParticiple, highlight
backgroundRepeat: "no-repeat", backgroundRepeat: "no-repeat",
backgroundPosition: hidePastParticiple ? "50% 45%" : "50% 35%", backgroundPosition: hidePastParticiple ? "50% 45%" : "50% 35%",
backgroundSize: "50%", backgroundSize: "50%",
}}> }}
>
{/* <div style={{ {/* <div style={{
fontSize: "larger", fontSize: "larger",
}}> }}>
@ -85,11 +101,14 @@ export function RootsAndStems({ textOptions, info, hidePastParticiple, highlight
{info.subDef} {info.subDef}
</div> </div>
} */} } */}
<div style={{ <div
style={{
border: "2px solid black", border: "2px solid black",
padding: "1rem", padding: "1rem",
margin: "0.25rem", margin: "0.25rem",
}} className="container"> }}
className="container"
>
<div className={rowClass + " align-items-center"}> <div className={rowClass + " align-items-center"}>
<div className={colClass}> <div className={colClass}>
<i className="fas fa-video fa-lg" /> <i className="fas fa-video fa-lg" />
@ -99,19 +118,26 @@ export function RootsAndStems({ textOptions, info, hidePastParticiple, highlight
<div> <div>
<i className="fas fa-camera fa-lg mx-3" /> <i className="fas fa-camera fa-lg mx-3" />
</div> </div>
{hasPerfectiveSplit && <div> {hasPerfectiveSplit && (
<div>
<button <button
className="btn btn-sm btn-outline-secondary" className="btn btn-sm btn-outline-secondary"
onClick={() => setSplit(!split)} onClick={() => setSplit(!split)}
> >
{split ? "join" : "split"} head {split ? "join" : "split"} head
</button> </button>
</div>} </div>
)}
</div> </div>
</div> </div>
</div> </div>
<div className={rowClass}> <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 style={title as any}>
<div>Imperfective Stem</div> <div>Imperfective Stem</div>
</div> </div>
@ -123,7 +149,10 @@ export function RootsAndStems({ textOptions, info, hidePastParticiple, highlight
/> />
</div> </div>
</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 style={title as any}>
<div>Perfective Stem</div> <div>Perfective Stem</div>
</div> </div>
@ -137,7 +166,12 @@ export function RootsAndStems({ textOptions, info, hidePastParticiple, highlight
</div> </div>
</div> </div>
<div className={rowClass}> <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 style={title as any}>
<div>Imperfective Root</div> <div>Imperfective Root</div>
</div> </div>
@ -148,7 +182,10 @@ export function RootsAndStems({ textOptions, info, hidePastParticiple, highlight
/> />
</div> </div>
</div> </div>
<div className={colClass} style={highlighted?.includes("perfective root") ? highlight : {}}> <div
className={colClass}
style={highlighted?.includes("perfective root") ? highlight : {}}
>
<div> <div>
<div style={title as any}> <div style={title as any}>
<div>Perfective Root</div> <div>Perfective Root</div>
@ -162,35 +199,45 @@ export function RootsAndStems({ textOptions, info, hidePastParticiple, highlight
</div> </div>
</div> </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> <div style={title as any}>Past Participle</div>
<VerbInfoItemDisplay <VerbInfoItemDisplay
item={pickPersInf(info.participle.past, persInf)} item={pickPersInf(info.participle.past, persInf)}
textOptions={textOptions} textOptions={textOptions}
/> />
</div>} </div>
)}
</div> </div>
</div> </div>
</div> </div>
); );
} }
function VerbInfo({ info, textOptions, showingStemsAndRoots, toggleShowingSar, highlightInRootsAndStems, hidePastParticiple, hideTypeInfo }: { function VerbInfo({
info: T.NonComboVerbInfo, info,
textOptions: T.TextOptions, textOptions,
showingStemsAndRoots: boolean, showingStemsAndRoots,
highlightInRootsAndStems?: T.RootsOrStemsToHighlight, toggleShowingSar,
toggleShowingSar: () => void, highlightInRootsAndStems,
hidePastParticiple?: boolean, hidePastParticiple,
hideTypeInfo?: boolean, 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; const inf = noPersInfs(info.root.imperfective).long;
return ( return (
<div className="my-3"> <div className="my-3">
{!hideTypeInfo && <VerbTypeInfo {!hideTypeInfo && <VerbTypeInfo info={info} textOptions={textOptions} />}
info={info}
textOptions={textOptions}
/>}
<Hider <Hider
showing={showingStemsAndRoots} showing={showingStemsAndRoots}
label={`🌳 Roots and Stems for ${inf.p}`} 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 ChartDisplay from "./VPChartDisplay";
import Hider from "../Hider"; import Hider from "../Hider";
import * as T from "../../../types"; import * as T from "../../../types";
import useStickyState from "../useStickyState"; 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 [showing, setShowing] = useStickyState<string[]>([], "VPTensesShowing");
const [showFormulas, setShowFormulas] = useStickyState<boolean>(false, "showFormulasWithCharts"); const [showFormulas, setShowFormulas] = useStickyState<boolean>(
false,
"showFormulasWithCharts"
);
const adjustShowing = (v: string) => { const adjustShowing = (v: string) => {
if (showing.includes(v)) { if (showing.includes(v)) {
setShowing(os => os.filter(x => x !== v)); setShowing((os) => os.filter((x) => x !== v));
} else { } else {
setShowing(os => [v, ...os]); setShowing((os) => [v, ...os]);
} }
} };
const options = VS.tenseCategory === "basic" const options =
VS.tenseCategory === "basic"
? verbTenseOptions ? verbTenseOptions
: VS.tenseCategory === "perfect" : VS.tenseCategory === "perfect"
? perfectTenseOptions ? perfectTenseOptions
: VS.tenseCategory === "modal" : VS.tenseCategory === "modal"
? abilityTenseOptions ? abilityTenseOptions
: imperativeTenseOptions; : imperativeTenseOptions;
const rawConjugations = conjugateVerb(VS.verb.entry, VS.verb.complement); // const rawConjugations = conjugateVerb(VS.verb.entry, VS.verb.complement);
const conjugations = ("stative" in rawConjugations) // const conjugations =
? rawConjugations[VS.isCompound === "stative" ? "stative" : "dynamic"] // "stative" in rawConjugations
: ("transitive" in rawConjugations) // ? rawConjugations[VS.isCompound === "stative" ? "stative" : "dynamic"]
? rawConjugations[VS.transitivity === "grammatically transitive" ? "grammaticallyTransitive" : "transitive"] // : "transitive" in rawConjugations
: rawConjugations; // ? rawConjugations[
function getTense(baseTense: T.VerbTense | T.PerfectTense | T.ImperativeTense): T.VerbTense | T.PerfectTense | T.ImperativeTense | T.AbilityTense { // VS.transitivity === "grammatically transitive"
return VS.tenseCategory === "modal" ? `${baseTense}Modal` as T.AbilityTense : baseTense; // ? "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> return (
<div className="clickable mb-2 small text-center" onClick={() => setShowFormulas(x => !x)}> <div>
<div
className="clickable mb-2 small text-center"
onClick={() => setShowFormulas((x) => !x)}
>
🧪 {!showFormulas ? "Show" : "Hide"} Formulas 🧪 {!showFormulas ? "Show" : "Hide"} Formulas
</div> </div>
{options.map((tense) => <div key={Math.random()}> {options.map((tense) => {
const t = getTense(tense.value);
return (
<div key={Math.random()}>
<Hider <Hider
label={tense.label} label={tense.label}
showing={showing.includes(tense.value)} showing={showing.includes(tense.value)}
handleChange={() => adjustShowing(tense.value)} handleChange={() => adjustShowing(tense.value)}
hLevel={5} hLevel={5}
> >
{showFormulas && <div className="mb-1"> {showFormulas && (
<div className="mb-1">
<samp>{tense.formula}</samp> <samp>{tense.formula}</samp>
</div>} </div>
)}
{showing && (
<ChartDisplay <ChartDisplay
conjugations={conjugations} verb={VS.verb}
tense={getTense(tense.value)} negative={VS.negative}
tense={t}
transitivity={VS.transitivity}
voice={VS.voice} voice={VS.voice}
imperative={isImperativeTense(t)}
opts={opts} opts={opts}
/> />
)}
</Hider> </Hider>
</div>)} </div>
</div>; );
})}
</div>
);
} }
export default AllTensesDisplay; 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 { import NewVerbFormDisplay from "./NewVerbFormDisplay";
getTenseVerbForm,
} from "../../../lib/src/phrase-building/vp-tools";
import VerbFormDisplay from "../VerbFormDisplay";
import * as T from "../../../types"; import * as T from "../../../types";
import { buildVerbChart } from "./chart-builder";
import { isPastTense } from "../../library";
function ChartDisplay({ conjugations, tense, opts, voice }: { function ChartDisplay({
conjugations: T.VerbConjugation, verb,
tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense, tense,
opts: T.TextOptions, opts,
voice: T.VerbSelection["voice"], 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); const verbChart = buildVerbChart({
return <div className="mb-4"> verb,
<VerbFormDisplay tense,
displayForm={form} voice,
showingFormInfo={false} negative,
textOptions={opts} transitivity,
info={conjugations.info} imperative,
});
return (
<div className="mb-4">
<NewVerbFormDisplay
chart={verbChart}
opts={opts}
transitivity={transitivity}
past={isPastTense(tense)}
/> />
</div>; </div>
);
} }
export default ChartDisplay; 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 // 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) { if ("mascSing" in s) {
// this path shouldn't be used, just for type safety // this path shouldn't be used, just for type safety
return s.mascSing; 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 { export function ensureNonComboVerbInfo(i: T.VerbInfo): T.NonComboVerbInfo {
return "stative" in i return "stative" in i ? i.stative : "transitive" in i ? i.transitive : 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 // @ts-ignore
if ("mascSing" in s) { if ("mascSing" in s) {
return s[persInf]; return s[persInf];
@ -82,7 +83,9 @@ export function getFirstSecThird(p: T.Person): 1 | 2 | 3 {
// return s; // 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) { if ("participle" in info) {
return ( return (
"mascSing" in info.root.perfective || "mascSing" in info.root.perfective ||
@ -92,15 +95,16 @@ export function hasPersInfs(info: T.NonComboVerbInfo | T.PassiveRootsAndStems |
); );
} }
return ( return (
"mascSing" in info.root.perfective || "mascSing" in info.root.perfective || "mascSing" in info.stem.perfective
"mascSing" in info.stem.perfective
); );
} }
// TODO: deprecated using new verb rendering thing // TODO: deprecated using new verb rendering thing
export function chooseParticipleInflection( export function chooseParticipleInflection(
pPartInfs: T.SingleOrLengthOpts<T.UnisexInflections> | T.SingleOrLengthOpts<T.PsString>, pPartInfs:
person: T.Person, | T.SingleOrLengthOpts<T.UnisexInflections>
| T.SingleOrLengthOpts<T.PsString>,
person: T.Person
): T.SingleOrLengthOpts<T.PsString> { ): T.SingleOrLengthOpts<T.PsString> {
if ("long" in pPartInfs) { if ("long" in pPartInfs) {
return { return {
@ -116,7 +120,10 @@ export function chooseParticipleInflection(
return pPartInfs; // already just one thing 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; const base = gender === "masc" ? 4 : 5;
return base + (number === "singular" ? 0 : 6); 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); return pos[0] + (pos[1] === 1 ? 6 : 0);
} }
export function getPersonInflectionsKey(person: T.Person): T.PersonInflectionsField { export function getPersonInflectionsKey(
return `${personGender(person)}${personIsPlural(person) ? "Plur" : "Sing"}` as T.PersonInflectionsField; person: T.Person
): T.PersonInflectionsField {
return `${personGender(person)}${
personIsPlural(person) ? "Plur" : "Sing"
}` as T.PersonInflectionsField;
} }
export function spaceInForm(form: T.FullForm<T.PsString>): boolean { 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(" "); return form.p.includes(" ");
} }
export function getPersonFromVerbForm(form: T.SingleOrLengthOpts<T.VerbBlock>, person: T.Person): T.SentenceForm { export function getPersonFromVerbForm(
return fmapSingleOrLengthOpts(x => { form: T.SingleOrLengthOpts<T.VerbBlock>,
person: T.Person
): T.SentenceForm {
return fmapSingleOrLengthOpts((x) => {
const [row, col] = getVerbBlockPosFromPerson(person); const [row, col] = getVerbBlockPosFromPerson(person);
return x[row][col]; return x[row][col];
}, form); }, form);
} }
export function getVerbBlockPosFromPerson(person: T.Person): [0 | 1 | 2 | 3 | 4 | 5, 0 | 1] { export function getVerbBlockPosFromPerson(
const plural = personIsPlural(person) person: T.Person
const row = (plural ? (person - 6) : person) as 0 | 1 | 2 | 3 | 4 | 5; ): [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; const col = plural ? 1 : 0;
return [row, col]; return [row, col];
} }
export function getAuxTransitivity(trans: T.Transitivity): "transitive" | "intransitive" { export function getAuxTransitivity(
trans: T.Transitivity
): "transitive" | "intransitive" {
return trans === "intransitive" ? "intransitive" : "transitive"; return trans === "intransitive" ? "intransitive" : "transitive";
} }
@ -169,29 +187,35 @@ export function personIsPlural(person: T.Person): boolean {
return person > 5; return person > 5;
} }
export function getEnglishPersonInfo(person: T.Person, version?: "short" | "long"): string { export function getEnglishPersonInfo(
const p = ([0,1,6,7].includes(person) person: T.Person,
version?: "short" | "long"
): string {
const p =
([0, 1, 6, 7].includes(person)
? "1st" ? "1st"
: [2, 3, 8, 9].includes(person) : [2, 3, 8, 9].includes(person)
? "2nd" ? "2nd"
: "3rd") + " pers."; : "3rd") + " pers.";
const number = personIsPlural(person) ? "plur" : "sing"; const number = personIsPlural(person) ? "plur" : "sing";
const n = version === "short" const n = version === "short" ? (number === "plur" ? "pl" : "sg") : number;
? (number === "plur" ? "pl" : "sg") : number;
const gender = personGender(person); const gender = personGender(person);
const g = version === "short" const g = version === "short" ? (gender === "masc" ? "m" : "f") : gender;
? (gender === "masc" ? "m" : "f")
: gender;
return `${p} ${n}. ${g}.`; return `${p} ${n}. ${g}.`;
} }
export function getEnglishGenNumInfo(gender: T.Gender, number: T.NounNumber): string { export function getEnglishGenNumInfo(
return `${gender === "masc" ? "masc" : "fem"} ${number === "plural" ? "plur." : "sing."}`; gender: T.Gender,
number: T.NounNumber
): string {
return `${gender === "masc" ? "masc" : "fem"} ${
number === "plural" ? "plur." : "sing."
}`;
} }
export function personToGenNum(p: T.Person): { export function personToGenNum(p: T.Person): {
gender: T.Gender, gender: T.Gender;
number: T.NounNumber, number: T.NounNumber;
} { } {
return { return {
gender: personGender(p), 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 number = personIsPlural(person) ? "plural" : "singular";
const n = version === "short" const n =
? (number === "plural" ? "plur." : "sing.") : number; version === "short" ? (number === "plural" ? "plur." : "sing.") : number;
const gender = personGender(person); const gender = personGender(person);
const g = gender; const g = gender;
return `${g}. ${n}`; return `${g}. ${n}`;
} }
export function randomNumber(minInclusive: number, maxExclusive: number): number { export function randomNumber(
return Math.floor(Math.random() * (maxExclusive - minInclusive) + minInclusive); minInclusive: number,
maxExclusive: number
): number {
return Math.floor(
Math.random() * (maxExclusive - minInclusive) + minInclusive
);
} }
export function randFromArray<M>(arr: M[]): M { export function randFromArray<M>(arr: Readonly<M[]>): M {
return arr[ return arr[Math.floor(Math.random() * arr.length)];
Math.floor(Math.random()*arr.length)
];
} }
export const isFirstPerson = (p: T.Person) => [0, 1, 6, 7].includes(p); 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]; 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) { if (!entry.c) {
return false; 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"; return "nounAdj";
} }
if (entry.c.slice(0, 3) === "v. ") { if (entry.c.slice(0, 3) === "v. ") {
@ -274,7 +310,7 @@ export function parseEc(ec: string): T.EnglishVerbConjugationEc {
if (s === "be") { if (s === "be") {
return ["am", "is", "being", "was", "been"]; 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); const b = s.slice(0, -1);
return [`${s}`, `${b}ies`, `${s}ing`, `${b}ied`, `${b}ied`]; 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); const b = s.slice(0, -2);
return [`${s}`, `${s}s`, `${b}ying`, `${s}d`, `${s}d`]; return [`${s}`, `${s}s`, `${b}ying`, `${s}d`, `${s}d`];
} }
const b = s === "" const b = s === "" ? "VERB" : s.slice(-1) === "e" ? s.slice(0, -1) : s;
? "VERB"
: (s.slice(-1) === "e")
? s.slice(0, -1)
: s;
return [`${s}`, `${s}s`, `${b}ing`, `${b}ed`, `${b}ed`]; return [`${s}`, `${s}s`, `${b}ing`, `${b}ed`, `${b}ed`];
} }
const items = ec.split(",").map(x => x.trim()); const items = ec.split(",").map((x) => x.trim());
return (items.length === 4) return items.length === 4
? [items[0], items[1], items[2], items[3], items[3]] ? [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]] ? [items[0], items[1], items[2], items[3], items[4]]
: makeRegularConjugations(items[0]); : 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 // @ts-ignore
if ("long" in x) { if ("long" in x) {
return x[length]; 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 { import {
getVerbBlockPosFromPerson, getVerbBlockPosFromPerson,
isSecondPerson, isSecondPerson,
personGender,
personNumber, personNumber,
personToGenNum, personToGenNum,
} from "../misc-helpers"; } from "../misc-helpers";
import { import { fmapSingleOrLengthOpts } from "../fmaps";
fmapSingleOrLengthOpts, import { concatPsString, getLength } from "../p-text-helpers";
} from "../fmaps";
import {
concatPsString,
getLength,
} from "../p-text-helpers";
import { import {
presentEndings, presentEndings,
pastEndings, pastEndings,
equativeEndings, equativeEndings,
imperativeEndings, imperativeEndings,
} from "../grammar-units"; } 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 { perfectTenseHasBa } from "../phrase-building/vp-tools";
import { makePsString, removeFVarients } from "../accent-and-ps-utils"; import { makePsString, removeFVarients } from "../accent-and-ps-utils";
import { getPastParticiple, getRootStem } from "./roots-and-stems"; import { getPastParticiple, getRootStem } from "./roots-and-stems";
import { isKedul, perfectTenseToEquative, verbEndingConcat } from "./rs-helpers"; import {
import { accentOnNFromEnd, accentPsSyllable, removeAccents } from "../accent-helpers"; isKedul,
perfectTenseToEquative,
verbEndingConcat,
} from "./rs-helpers";
import {
accentOnNFromEnd,
accentPsSyllable,
removeAccents,
} from "../accent-helpers";
const formulas: Record<T.VerbTense | T.ImperativeTense, { const formulas: Record<
aspect: T.Aspect, T.VerbTense | T.ImperativeTense,
tenseC: "past" | "present" | "imperative", {
hasBa: boolean, aspect: T.Aspect;
}> = { tenseC: "past" | "present" | "imperative";
"presentVerb": { hasBa: boolean;
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,
},
} }
> = {
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: // TODO: dynamic and stative compounds
// 1. get the conjugation for all persons export function renderVerb({
// 2. if transitive present tense, check (or do all the conjugation) the conjugation with all different complement person stuff verb,
// if necessary pull out the object option tense,
// subject,
object,
// to make the verbs displayable for the charts voice,
// - take the output of renderVerb { hasBa, VerbRenderedOutput } }: {
// - filter out a long and short version etc if necessary verb: T.VerbEntry;
// - pass it into combineIntoText negative: boolean;
tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense; // TODO: make T.Tense
// PROBLEM: how to handle when to specify the object subject: T.Person;
// present tense object: T.Person | undefined;
voice: T.Voice;
// TODO: problem with laaR - no perfective split }): T.RenderVerbOutput {
export function renderVerb({ verb, tense: tense, person, voice, negative, complementGenNum }: {
verb: T.VerbEntry,
negative: boolean,
tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense, // TODO: make T.Tense
person: T.Person,
complementGenNum: { gender: T.Gender, number: T.NounNumber },
voice: T.Voice,
}): {
hasBa: boolean,
vbs: T.VerbRenderedOutput,
} {
if (isPerfectTense(tense)) { 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 { aspect, tenseC, hasBa } = formulas[removeAbility(tense)];
const isPast = tenseC === "past"; const isPast = tenseC === "past";
const type = isAbilityTense(tense) ? "ability" : "basic"; const type = isAbilityTense(tense) ? "ability" : "basic";
const transitive = object !== undefined;
const king = transitive && isPast ? object : subject;
// #1 get the appropriate root / stem // #1 get the appropriate root / stem
const [vHead, rest] = getRootStem({ const [vHead, rest] = getRootStem({
@ -123,20 +129,22 @@ export function renderVerb({ verb, tense: tense, person, voice, negative, comple
aspect, aspect,
voice, voice,
type, type,
genderNumber: complementGenNum, genderNumber: personToGenNum(transitive ? object : subject),
}); });
// #2 add the verb ending to it // #2 add the verb ending to it
const ending = getEnding(person, tenseC, aspect); const ending = getEnding(king, tenseC, aspect);
return { return {
hasBa, hasBa,
objComp: undefined,
vbs: [ vbs: [
vHead, vHead,
addEnding({ addEnding({
rs: rest, rs: rest,
ending, ending,
verb, verb,
person, person: king,
pastThird: isPast && person === T.Person.ThirdSingMale, pastThird: isPast && king === T.Person.ThirdSingMale,
aspect, aspect,
basicForm: type === "basic" && voice === "active", 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 }: { function renderPerfectVerb({
tense: T.PerfectTense, tense,
verb: T.VerbEntry, verb,
voice: T.Voice, voice,
person: T.Person, person,
// TODO: Tighter typing on the output for T.VB (enforce genderNumber?) }: {
}): { hasBa: boolean, vbs: [[], [T.VB, T.VBE]] } { 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); const hasBa = perfectTenseHasBa(tense);
// #1 get the past participle // #1 get the past participle
const pp = getPastParticiple(verb, voice, personToGenNum(person)); const pp = getPastParticiple(verb, voice, personToGenNum(person));
@ -160,27 +176,39 @@ function renderPerfectVerb({ tense, verb, voice, person }: {
const equativeBlock: T.VBE = { const equativeBlock: T.VBE = {
type: "VB", type: "VB",
person, person,
ps: fmapSingleOrLengthOpts(x => x[row][col], equative), ps: fmapSingleOrLengthOpts((x) => x[row][col], equative),
}; };
return { return {
hasBa, hasBa,
objComp: undefined,
vbs: [[], [pp, equativeBlock]], vbs: [[], [pp, equativeBlock]],
}; };
} }
function addEnding({ verb, rs, ending, person, pastThird, aspect, basicForm }: { function addEnding({
rs: [T.VB, T.VBA] | [T.VBA], verb,
ending: T.SingleOrLengthOpts<T.PsString[]>, rs,
person: T.Person, ending,
verb: T.VerbEntry, person,
pastThird: boolean, pastThird,
aspect: T.Aspect, aspect,
basicForm: boolean, 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] { }): [T.VB, T.VBE] | [T.VBE] {
return rs.length === 2 return rs.length === 2
? [rs[0], addEnd(rs[1], ending)] ? [rs[0], addEnd(rs[1], ending)]
: [addEnd(rs[0], 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") { if (vba.type === "welded") {
return { return {
...vba, ...vba,
@ -191,9 +219,12 @@ function addEnding({ verb, rs, ending, person, pastThird, aspect, basicForm }: {
return { return {
...addToVBBasicEnd(vba, ending), ...addToVBBasicEnd(vba, ending),
person, person,
};
} }
} function addToVBBasicEnd(
function addToVBBasicEnd(vb: T.VBBasic, end: T.SingleOrLengthOpts<T.PsString[]>): T.VBBasic { vb: T.VBBasic,
end: T.SingleOrLengthOpts<T.PsString[]>
): T.VBBasic {
if ("long" in vb.ps) { if ("long" in vb.ps) {
if (vb.ps.short[0].f === "ghl" && pastThird && basicForm) { if (vb.ps.short[0].f === "ghl" && pastThird && basicForm) {
return { return {
@ -208,18 +239,23 @@ function addEnding({ verb, rs, ending, person, pastThird, aspect, basicForm }: {
...vb, ...vb,
ps: { ps: {
long: verbEndingConcat(vb.ps.long, endLong), long: verbEndingConcat(vb.ps.long, endLong),
short: pastThird && basicForm short:
pastThird && basicForm
? ensure3rdPast(vb.ps.short, endShort, verb, aspect) ? ensure3rdPast(vb.ps.short, endShort, verb, aspect)
: verbEndingConcat(vb.ps.short, endShort), : verbEndingConcat(vb.ps.short, endShort),
...vb.ps.mini ? { ...(vb.ps.mini
? {
mini: verbEndingConcat(vb.ps.mini, endShort), mini: verbEndingConcat(vb.ps.mini, endShort),
} : {}, }
: {}),
}, },
}; };
} }
/* istanbul ignore next */ /* istanbul ignore next */
if ("long" in end) { 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 { return {
...vb, ...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 (tenseC === "imperative") {
if (!isSecondPerson(person)) { if (!isSecondPerson(person)) {
throw new Error("imperative forms must be second 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 number = personNumber(person);
const ends = imperativeEndings[0][number === "singular" ? 0 : 1]; const ends = imperativeEndings[0][number === "singular" ? 0 : 1];
return aspect === "imperfective" return aspect === "imperfective"
? ends.map(e => accentPsSyllable(e)) ? ends.map((e) => accentPsSyllable(e))
: ends; : ends;
} }
const [row, col] = getVerbBlockPosFromPerson(person); const [row, col] = getVerbBlockPosFromPerson(person);
return tenseC === "past" ? { return tenseC === "past"
? {
long: pastEndings.long[row][col], long: pastEndings.long[row][col],
short: pastEndings.short[row][col], short: pastEndings.short[row][col],
} : presentEndings[row][col]; }
: presentEndings[row][col];
} }
// TODO: THIS IS PROBABLY SKEEEETCH // 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)) { if (isKedul(verb)) {
return aspect === "perfective" return aspect === "perfective"
? [{ p: "شو", f: "sho" }] ? [{ p: "شو", f: "sho" }]
@ -263,18 +310,27 @@ function ensure3rdPast(rs: T.PsString[], ending: T.PsString[], verb: T.VerbEntry
if (isTlulVerb(verb)) { if (isTlulVerb(verb)) {
// should be imperfective at this point // should be imperfective at this point
// the perfective غی should already be covered in the function this is coming from // the perfective غی should already be covered in the function this is coming from
return [{ return [
{
p: rs[0].p.slice(0, -1) + "ه", p: rs[0].p.slice(0, -1) + "ه",
f: rs[0].f.slice(0, -2) + "ú", 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) { if (verb.entry.tppp && verb.entry.tppf) {
const tip = removeAccents(verb.entry.separationAtP !== undefined const tip = removeAccents(
? makePsString(verb.entry.tppp.slice(verb.entry.separationAtP), verb.entry.tppf.slice(verb.entry.separationAtF)) verb.entry.separationAtP !== undefined
: makePsString(verb.entry.tppp, verb.entry.tppf)); ? makePsString(
const aTip = aspect === "imperfective" verb.entry.tppp.slice(verb.entry.separationAtP),
? accentOnNFromEnd(tip, 0) verb.entry.tppf.slice(verb.entry.separationAtF)
: tip; )
: makePsString(verb.entry.tppp, verb.entry.tppf)
);
const aTip = aspect === "imperfective" ? accentOnNFromEnd(tip, 0) : tip;
return [aTip]; return [aTip];
// if it ends in a consonant, the special form will also have another // if it ends in a consonant, the special form will also have another
// variation ending with a ه - u // variation ending with a ه - u
@ -286,18 +342,17 @@ function ensure3rdPast(rs: T.PsString[], ending: T.PsString[], verb: T.VerbEntry
// ] : [], // ] : [],
// ]; // ];
} }
const endsInAwul = ( const endsInAwul =
(["awul", "awúl"].includes(removeFVarients(verb.entry.f).slice(-4))) ["awul", "awúl"].includes(removeFVarients(verb.entry.f).slice(-4)) &&
&& verb.entry.p.slice(-2) === "ول";
(verb.entry.p.slice(-2) === "ول")
);
// TODO: check about verbs like tawul (if they exist) // TODO: check about verbs like tawul (if they exist)
if (endsInAwul) { if (endsInAwul) {
const base = { p: rs[0].p.slice(0, -1), f: rs[0].f.slice(0, -2) }; 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" }); const aawuEnd = concatPsString(base, {
return [aspect === "imperfective" p: "اوه",
? accentOnNFromEnd(aawuEnd, 0) f: base.f.charAt(base.f.length - 1) === "a" ? "awu" : "aawu",
: aawuEnd]; });
return [aspect === "imperfective" ? accentOnNFromEnd(aawuEnd, 0) : aawuEnd];
} }
const endsInDental = ["د", "ت"].includes(rs[0].p.slice(-1)); const endsInDental = ["د", "ت"].includes(rs[0].p.slice(-1));
// short endings like ورسېد // short endings like ورسېد
@ -305,6 +360,8 @@ function ensure3rdPast(rs: T.PsString[], ending: T.PsString[], verb: T.VerbEntry
return verbEndingConcat(rs, ends); 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; 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 { import { concatPsString, trimOffPs } from "../p-text-helpers";
concatPsString, trimOffPs,
} from "../p-text-helpers";
import * as T from "../../../types"; import * as T from "../../../types";
import { makePsString, removeFVarientsFromVerb } from "../accent-and-ps-utils"; 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 { 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 { inflectPattern3 } from "./new-inflectors";
import { fmapSingleOrLengthOpts } from "../fmaps"; import { fmapSingleOrLengthOpts } from "../fmaps";
const statVerb = { 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"}), intransitive: vEntry({
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"}), 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 = { const shwulVB: T.VBBasic = {
@ -28,23 +78,30 @@ const shwulVB: T.VBBasic = {
long: [{ p: "شول", f: "shwul" }], long: [{ p: "شول", f: "shwul" }],
short: [{ p: "شو", f: "shw" }], short: [{ p: "شو", f: "shw" }],
}, },
} };
const shVB: T.VBBasic = { const shVB: T.VBBasic = {
type: "VB", type: "VB",
ps: [{ p: "ش", f: "sh" }], ps: [{ p: "ش", f: "sh" }],
} };
// TODO: figure out how to handle dynamic / stative verbs // TODO: figure out how to handle dynamic / stative verbs
export function getRootStem({ verb, rs, aspect, type, genderNumber, voice }: { export function getRootStem({
verb: T.VerbEntry, verb,
rs: "root" | "stem", rs,
aspect: T.Aspect, aspect,
voice: T.Voice, type,
type: "basic" | "ability", genderNumber,
voice,
}: {
verb: T.VerbEntry;
rs: "root" | "stem";
aspect: T.Aspect;
voice: T.Voice;
type: "basic" | "ability";
genderNumber: { genderNumber: {
gender: T.Gender, gender: T.Gender;
number: T.NounNumber, number: T.NounNumber;
}, };
}): T.RootsStemsOutput { }): T.RootsStemsOutput {
const v = removeFVarientsFromVerb(verb); const v = removeFVarientsFromVerb(verb);
if (type === "ability") { if (type === "ability") {
@ -63,22 +120,23 @@ function getAbilityRs(
aspect: T.Aspect, aspect: T.Aspect,
rs: "root" | "stem", rs: "root" | "stem",
voice: T.Voice, voice: T.Voice,
genderNum: T.GenderNumber, genderNum: T.GenderNumber
): [[] | [T.VHead], [T.VB, T.VBA]] { ): [[] | [T.VHead], [T.VB, T.VBA]] {
const losesAspect = isTlulVerb(verb) || (isStatComp(verb) && vTransitivity(verb) === "intransitive"); const losesAspect =
const [vhead, [basicroot]] = voice === "passive" isTlulVerb(verb) ||
(isStatComp(verb) && vTransitivity(verb) === "intransitive");
const [vhead, [basicroot]] =
voice === "passive"
? getPassiveRs(verb, "imperfective", "root", genderNum) ? getPassiveRs(verb, "imperfective", "root", genderNum)
: getRoot(verb, genderNum, losesAspect ? "imperfective" : aspect); : getRoot(verb, genderNum, losesAspect ? "imperfective" : aspect);
return [ return [vhead, [addAbilityEnding(basicroot), rs === "root" ? shwulVB : shVB]];
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); const v = removeFVarientsFromVerb(verb);
if (voice === "passive") { if (voice === "passive") {
return getPassivePp(v, { gender, number }); 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) { if (isStatComp(v) && v.complement) {
return weld( return weld(
makeComplement(v.complement, { gender, number }), makeComplement(v.complement, { gender, number }),
getPastParticiple( getPastParticiple(statVerb[vTransitivity(verb)], voice, {
statVerb[vTransitivity(verb)], gender,
voice, number,
{ gender, number }, }) as T.VBGenNum
) as T.VBGenNum,
); );
} }
if (verb.entry.pprtp && verb.entry.pprtf) { if (verb.entry.pprtp && verb.entry.pprtf) {
@ -102,7 +159,11 @@ export function getPastParticiple(verb: T.VerbEntry, voice: T.Voice, { gender, n
number, 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 longRoot = getLongVB(basicRoot);
const rootWLengths = possiblePPartLengths(longRoot); const rootWLengths = possiblePPartLengths(longRoot);
/* istanbul ignore next */ /* istanbul ignore next */
@ -116,7 +177,9 @@ export function getPastParticiple(verb: T.VerbEntry, voice: T.Voice, { gender, n
number, 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) => { return fmapSingleOrLengthOpts((x) => {
const withTail = concatPsString(x[0], { p: "ی", f: "ey" }); const withTail = concatPsString(x[0], { p: "ی", f: "ey" });
return inflectPattern3(withTail, { gender, number }); 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) { if (isStatComp(verb) && verb.complement) {
return weld( return weld(
makeComplement(verb.complement, genderNumber), 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 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); 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 [vHead, [basicRoot]] = getRoot(verb, genderNumber, aspect);
const longRoot = getLongVB(basicRoot); const longRoot = getLongVB(basicRoot);
const kedulVba = getRootStem({ verb: statVerb.intransitive, aspect, rs, type: "basic", voice: "active", genderNumber: { gender: "masc", number: "singular" }})[1][0] as T.VBBasic; const kedulVba = getRootStem({
return [ verb: statVerb.intransitive,
vHead, aspect,
[weld(longRoot, kedulVba)], 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]] { function getRoot(
if (verb.complement && isStatComp(verb) && (aspect === "perfective" || statCompImperfectiveSpace(verb))) { 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( const auxStem = getRoot(
statVerb[vTransitivity(verb)], statVerb[vTransitivity(verb)],
genderNum, genderNum,
aspect, aspect
)[1][0] as T.VBBasic; )[1][0] as T.VBBasic;
const complement = makeComplement(verb.complement, genderNum); const complement = makeComplement(verb.complement, genderNum);
return aspect === "perfective" return aspect === "perfective"
? [[complement], [auxStem]] ? [[complement], [auxStem]]
: [[], [weld(complement, auxStem)]]; : [[], [weld(complement, auxStem)]];
} }
const base = aspect === "imperfective" const base =
aspect === "imperfective"
? accentOnNFromEnd(makePsString(verb.entry.p, verb.entry.f), 0) ? accentOnNFromEnd(makePsString(verb.entry.p, verb.entry.f), 0)
: removeAccents( : removeAccents(
(verb.entry.prp && verb.entry.prf) verb.entry.prp && verb.entry.prf
? makePsString(verb.entry.prp, verb.entry.prf) ? makePsString(verb.entry.prp, verb.entry.prf)
: makePsString(verb.entry.p, verb.entry.f) : makePsString(verb.entry.p, verb.entry.f)
); );
const [perfectiveHead, rest] = aspect === "perfective" const [perfectiveHead, rest] =
? getPerfectiveHead(base, verb) aspect === "perfective" ? getPerfectiveHead(base, verb) : [undefined, base];
: [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 [ return [
perfectiveHead ? [perfectiveHead] : [], perfectiveHead ? [perfectiveHead] : [],
[ [
{ {
type: "VB", type: "VB",
ps: aspect === "imperfective" ps:
aspect === "imperfective"
? { ? {
long: [rest], long: [rest],
short: [addTrailingAccent(removeL(rest))], short: [addTrailingAccent(removeL(rest))],
@ -182,22 +288,32 @@ function getRoot(verb: T.VerbEntryNoFVars, genderNum: T.GenderNumber, aspect: T.
: { : {
long: [rest], long: [rest],
short: [removeL(rest)], short: [removeL(rest)],
...(aspect === "perfective" && isKawulVerb(verb)) ? { ...(aspect === "perfective" && isKawulVerb(verb)
? {
mini: [{ p: "ک", f: "k" }], 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); const statComp = isStatComp(verb);
if (verb.complement && statComp && (aspect === "perfective" || statCompImperfectiveSpace(verb))) { if (
verb.complement &&
statComp &&
(aspect === "perfective" || statCompImperfectiveSpace(verb))
) {
const auxStem = getStem( const auxStem = getStem(
statVerb[vTransitivity(verb)], statVerb[vTransitivity(verb)],
genderNum, genderNum,
aspect, aspect
)[1][0] as T.VBBasic; )[1][0] as T.VBBasic;
const complement = makeComplement(verb.complement, genderNum); const complement = makeComplement(verb.complement, genderNum);
return aspect === "perfective" return aspect === "perfective"
@ -208,17 +324,22 @@ function getStem(verb: T.VerbEntryNoFVars, genderNum: T.GenderNumber, aspect: T.
if (verb.entry.f === "tlul") { if (verb.entry.f === "tlul") {
return tlulPerfectiveStem(genderNum); return tlulPerfectiveStem(genderNum);
} }
if (!isKedul(verb) && vTransitivity(verb) === "intransitive" && verb.entry.p.endsWith("ېدل")) { if (
return splitEdulIntans(edulIntransBase(verb)) !isKedul(verb) &&
vTransitivity(verb) === "intransitive" &&
verb.entry.p.endsWith("ېدل")
) {
return splitEdulIntans(edulIntransBase(verb));
} }
const base: T.PsString = (verb.entry.ssp && verb.entry.ssf) const base: T.PsString =
// with irregular perfective stem verb.entry.ssp && verb.entry.ssf
? makePsString(verb.entry.ssp, verb.entry.ssf) ? // with irregular perfective stem
: (verb.entry.psp && verb.entry.psf) makePsString(verb.entry.ssp, verb.entry.ssf)
// with perfective stem based on irregular perfective root : verb.entry.psp && verb.entry.psf
? makePsString(verb.entry.psp, verb.entry.psf) ? // with perfective stem based on irregular perfective root
// with regular infinitive based perfective stem makePsString(verb.entry.psp, verb.entry.psf)
: removeL(makePsString(verb.entry.p, verb.entry.f)); : // with regular infinitive based perfective stem
removeL(makePsString(verb.entry.p, verb.entry.f));
const [perfectiveHead, rest] = getPerfectiveHead(base, verb); const [perfectiveHead, rest] = getPerfectiveHead(base, verb);
return [ return [
perfectiveHead ? [perfectiveHead] : [], 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 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)] ? [makePsString(verb.entry.psp, verb.entry.psf)]
: (vTransitivity(verb) === "intransitive" && rawBase.p.endsWith("ېد")) : vTransitivity(verb) === "intransitive" && rawBase.p.endsWith("ېد")
? edulIntransBase(verb) ? edulIntransBase(verb)
: isKawulVerb(verb) || statComp || (countSyllables(rawBase) > 1 && rawBase.f.endsWith("aw")) : isKawulVerb(verb) ||
statComp ||
(countSyllables(rawBase) > 1 && rawBase.f.endsWith("aw"))
? [addTrailingAccent(rawBase)] ? [addTrailingAccent(rawBase)]
: [rawBase]; : [rawBase];
return [ 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]] { function splitEdulIntans(
const [ph, long] = ("long" in ps) ps: T.SingleOrLengthOpts<T.PsString[]>
): [[T.PH] | [], [T.VB]] {
const [ph, long] =
"long" in ps
? getPerfectiveHead(ps.long[0], verb) ? getPerfectiveHead(ps.long[0], verb)
: getPerfectiveHead(ps[0], verb) : getPerfectiveHead(ps[0], verb);
const short = ("long" in ps) const short =
? getPerfectiveHead(ps.short[0], verb) "long" in ps ? getPerfectiveHead(ps.short[0], verb) : undefined;
: undefined;
if (short) { if (short) {
return [ return [
ph ? [ph] : [], ph ? [ph] : [],
@ -268,27 +394,28 @@ function getStem(verb: T.VerbEntryNoFVars, genderNum: T.GenderNumber, aspect: T.
], ],
]; ];
} }
return [ return [ph ? [ph] : [], [{ type: "VB", ps: [long] }]];
ph ? [ph] : [],
[
{ type: "VB", ps: [long] },
],
];
} }
} }
// TODO: This is a nasty and messy way to do it with the length options included // 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) { // if ((verb.entry.ssp && verb.entry.ssf) || verb.entry.separationAtP) {
// // handle split // // handle split
// } // }
if (v.entry.separationAtP && v.entry.separationAtF) { if (v.entry.separationAtP && v.entry.separationAtF) {
const ph: T.PH = { const ph: T.PH = {
type: "PH", type: "PH",
ps: accentOnNFromEnd({ ps: accentOnNFromEnd(
{
p: base.p.slice(0, v.entry.separationAtP), p: base.p.slice(0, v.entry.separationAtP),
f: base.f.slice(0, v.entry.separationAtF), f: base.f.slice(0, v.entry.separationAtF),
}, 0), },
0
),
}; };
const rest = { const rest = {
p: base.p.slice(v.entry.separationAtP), 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 const [ph, rest]: [T.PH | undefined, T.PsString] = v.entry.noOo
? [undefined, base] ? [undefined, base]
: v.entry.sepOo : 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" : ["آ", "ا"].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)) : ["ó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) === "ای" : ["ée", "ee"].includes(base.f.slice(0, 2)) && base.p.slice(0, 2) === "ای"
? [ ? [
{ type: "PH", ps: { p: "وي", f: "wée" } }, { 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), p: base.p.slice(2),
f: base.f.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é" } }, { type: "PH", ps: { p: "وي", f: "wé" } },
{ {
p: base.p.slice(2), p: base.p.slice(2),
f: base.f.slice(1), f: base.f.slice(1),
}, },
] : ["ó", "o"].includes(base.f[0]) && base.p.slice(0, 2) === "او" ]
? [ : ["ó", "o"].includes(base.f[0]) && base.p.slice(0, 2) === "او"
{ type: "PH", ps: { p: "و", f: "óo`"}}, ? [{ type: "PH", ps: { p: "و", f: "óo`" } }, base]
base, : [{ type: "PH", ps: { p: "و", f: "óo" } }, base];
] : [ return [ph, removeAccents(rest)];
{ type: "PH", ps: { p: "و", f: "óo" }},
base,
];
return [
ph,
removeAccents(rest),
];
function removeAStart(ps: T.PsString) { function removeAStart(ps: T.PsString) {
return { return {
p: ps.p.slice(1), 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 base = trimOffPs(makePsString(v.entry.p, v.entry.f), 3, 4);
const long: T.PsString[] = [concatPsString( const long: T.PsString[] = [concatPsString(base, { p: "ېږ", f: "éG" })];
base, const short: T.PsString[] | undefined = v.entry.shortIntrans
{ p: "ېږ", f: "éG" },
)];
const short: T.PsString[] | undefined = (v.entry.shortIntrans)
? [base] ? [base]
: undefined; : undefined;
return short ? { short, long } : long; return short ? { short, long } : long;

View File

@ -1,13 +1,12 @@
import * as T from "../../../types"; import * as T from "../../../types";
import { import {
capitalizeFirstLetter, capitalizeFirstLetter,
concatPsString, getLong, concatPsString,
getLong,
} from "../p-text-helpers"; } from "../p-text-helpers";
import { negativeParticle } from "../grammar-units"; import { negativeParticle } from "../grammar-units";
import * as grammarUnits from "../grammar-units"; import * as grammarUnits from "../grammar-units";
import { import { removeDuplicates } from "./vp-tools";
removeDuplicates,
} from "./vp-tools";
import { getEnglishFromRendered, getPashtoFromRendered } from "./np-tools"; import { getEnglishFromRendered, getPashtoFromRendered } from "./np-tools";
import { completeEPSelection, renderEP } from "./render-ep"; import { completeEPSelection, renderEP } from "./render-ep";
import { completeVPSelection } from "./vp-tools"; import { completeVPSelection } from "./vp-tools";
@ -22,18 +21,15 @@ import {
isRenderedVerbB, isRenderedVerbB,
specifyEquativeLength, specifyEquativeLength,
} from "./blocks-utils"; } from "./blocks-utils";
import { import { blank, kidsBlank } from "../misc-helpers";
blank,
kidsBlank,
} from "../misc-helpers";
type BlankoutOptions = { type BlankoutOptions = {
equative?: boolean, equative?: boolean;
ba?: boolean, ba?: boolean;
kidsSection?: boolean, kidsSection?: boolean;
verb?: boolean, verb?: boolean;
negative?: boolean, negative?: boolean;
predicate?: boolean, predicate?: boolean;
}; };
// function compilePs({ blocks, kids, verb: { head, rest }, VP }: CompilePsInput): T.SingleOrLengthOpts<T.PsString[]> { // 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): {
export function compileEP(EP: T.EPRendered, combineLengths: true, blankOut?: BlankoutOptions): { ps: T.PsString[], e?: string[] }; ps: T.SingleOrLengthOpts<T.PsString[]>;
export function compileEP(EP: T.EPRendered, combineLengths?: boolean, blankOut?: BlankoutOptions): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string[] } { 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); const psResult = compileEPPs(EP.blocks, EP.kids, EP.omitSubject, blankOut);
return { return {
ps: combineLengths ? flattenLengths(psResult) : psResult, 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(
export function compileVP(VP: T.VPRendered, form: T.FormVersion, combineLengths: true, blankOut?: BlankoutOptions): { ps: T.PsString[], e?: string [] }; VP: T.VPRendered,
export function compileVP(VP: T.VPRendered, form: T.FormVersion, combineLengths?: true, blankOut?: BlankoutOptions): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] } { 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 verb = getVerbFromBlocks(VP.blocks).block;
const psResult = compileVPPs(VP.blocks, VP.kids, form, VP.king, blankOut); const psResult = compileVPPs(VP.blocks, VP.kids, form, VP.king, blankOut);
return { return {
ps: combineLengths ? flattenLengths(psResult) : psResult, ps: combineLengths ? flattenLengths(psResult) : psResult,
// TODO: English doesn't quite work for dynamic compounds in passive voice // 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[] { function compileVPPs(
const subjectPerson = getSubjectSelectionFromBlocks(blocks) blocks: T.Block[][],
.selection.selection.person; kids: T.Kid[],
form: T.FormVersion,
king: "subject" | "object",
blankOut?: BlankoutOptions
): T.PsString[] {
const subjectPerson =
getSubjectSelectionFromBlocks(blocks).selection.selection.person;
const blocksWKids = putKidsInKidsSection( const blocksWKids = putKidsInKidsSection(
filterForVisibleBlocksVP(blocks, form, king), filterForVisibleBlocksVP(blocks, form, king),
kids, kids,
!!blankOut?.ba !!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)) { if (hasEquativeWithLengths(blocks)) {
return { return {
long: compileEPPs(specifyEquativeLength(blocks, "long"), kids, omitSubject, blankOut) as T.PsString[], long: compileEPPs(
short: compileEPPs(specifyEquativeLength(blocks, "short"), kids, omitSubject, blankOut) as T.PsString[], specifyEquativeLength(blocks, "long"),
kids,
omitSubject,
blankOut
) as T.PsString[],
short: compileEPPs(
specifyEquativeLength(blocks, "short"),
kids,
omitSubject,
blankOut
) as T.PsString[],
}; };
} }
const subjectPerson = getSubjectSelectionFromBlocks(blocks) const subjectPerson =
.selection.selection.person; getSubjectSelectionFromBlocks(blocks).selection.selection.person;
const blocksWKids = putKidsInKidsSection( 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, kids,
!!blankOut?.kidsSection !!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"; const servant = king === "object" ? "subject" : "object";
return blocks.map(blks => blks.filter((block) => { return blocks.map((blks) =>
blks.filter((block) => {
if (form.removeKing) { if (form.removeKing) {
if ( if (
(king === "subject" && block.block.type === "subjectSelection") (king === "subject" && block.block.type === "subjectSelection") ||
||
(king === "object" && block.block.type === "objectSelection") (king === "object" && block.block.type === "objectSelection")
) return false; )
return false;
} }
if (form.shrinkServant) { if (form.shrinkServant) {
if ( if (
(servant === "subject" && block.block.type === "subjectSelection") (servant === "subject" && block.block.type === "subjectSelection") ||
||
(servant === "object" && block.block.type === "objectSelection") (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 false;
} }
return true; 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; if (!omitSubject) return blocks;
return blocks.map(blks => blks.filter(({ block }) => { return blocks.map((blks) =>
blks.filter(({ block }) => {
if (block.type === "subjectSelection") { if (block.type === "subjectSelection") {
return false; return false;
} }
return true; 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 { function removeDoubleBlanks(x: T.PsString): T.PsString {
return { return {
p: x.p.replace(blank.p + blank.p, blank.p), 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 first = pieces[0];
const next = pieces[1]; const next = pieces[1];
const rest = pieces.slice(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] ? [first]
: ( : (blankOut?.equative &&
(blankOut?.equative && "block" in first && first.block.type === "equative") || "block" in first &&
first.block.type === "equative") ||
(blankOut?.verb && "block" in first && isRenderedVerbB(first)) || (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] ? [blank]
: ((blankOut?.ba) && "kid" in first && first.kid.type === "ba") : blankOut?.ba && "kid" in first && first.kid.type === "ba"
? [kidsBlank] ? [kidsBlank]
: (blankOut?.negative && "block" in first && first.block.type === "negative") : blankOut?.negative &&
"block" in first &&
first.block.type === "negative"
? [{ p: "", f: "" }] ? [{ p: "", f: "" }]
: getPsFromPiece(first, subjectPerson); : getPsFromPiece(first, subjectPerson);
if (!rest.length) { if (!rest.length) {
return firstPs; return firstPs;
} }
return combine(rest).flatMap(r => ( return combine(rest)
firstPs.map(fPs => concatPsString( .flatMap((r) =>
firstPs.map((fPs) =>
concatPsString(
fPs, fPs,
// TODO: this spacing is a mess and not accurate // 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"))) !("p" in first) &&
? ((("block" in next && next.block.type === "negative") || ("kid" in next && next.kid.type === "mini-pronoun")) ? { p: "", f: " " } : "") "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); 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 ("block" in piece) {
if (piece.block.type === "negative") { if (piece.block.type === "negative") {
return [ return [
negativeParticle[piece.block.imperative ? "imperative" : "nonImperative"], negativeParticle[
piece.block.imperative ? "imperative" : "nonImperative"
],
]; ];
} }
if (piece.block.type === "equative") { if (piece.block.type === "equative") {
// length will already be specified in compileEPPs - this is just for type safety // length will already be specified in compileEPPs - this is just for type safety
return getLong(piece.block.equative.ps); 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); return getPashtoFromRendered(piece.block.selection, subjectPerson);
} }
if (piece.block.type === "AP") { 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.type === "complement") {
if (piece.block.selection.type === "sandwich") { if (piece.block.selection.type === "sandwich") {
// TODO: Kinda cheating // 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; 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 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") { if (v.type === "VB") {
return flattenLengths(v.ps); return flattenLengths(v.ps);
} }
@ -252,11 +353,9 @@ function getPsFromWelded(v: T.Welded): T.PsString[] {
} }
const left = getPsFromSide(v.left); const left = getPsFromSide(v.left);
const right = getPsFromSide(v.right); const right = getPsFromSide(v.right);
return left.flatMap(leftVar => ( return left.flatMap((leftVar) =>
right.flatMap(rightVar => ( right.flatMap((rightVar) => concatPsString(leftVar, " ", rightVar))
concatPsString(leftVar, " ", rightVar) );
))
));
} }
function getEngAPs(blocks: T.Block[][]): string { function getEngAPs(blocks: T.Block[][]): string {
@ -279,13 +378,17 @@ function getEngComplement(blocks: T.Block[][]): string | undefined {
return comp.selection.e; 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)[] { function insert(blocks: T.Block[]): (T.Block | T.Kid | T.PsString)[] {
const first = blocks[0]; const first = blocks[0];
const rest = blocks.slice(1); const rest = blocks.slice(1);
return [ return [
first, first,
...enforceKidsSectionBlankout ? [kidsBlank] : kids, ...(enforceKidsSectionBlankout ? [kidsBlank] : kids),
...rest, ...rest,
]; ];
} }
@ -293,40 +396,65 @@ function putKidsInKidsSection(blocksWVars: T.Block[][], kids: T.Kid[], enforceKi
} }
function compileEnglishVP(VP: T.VPRendered): string[] | undefined { 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 { function insertEWords(
return e e: string,
.replace("$SUBJ", subject) {
.replace("$OBJ", object || "") 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) // add the complement in English if it's an external complement from a helper verb (kawul/kedul)
// TODO! // TODO!
+ (complement /* && isStativeHelper(getVerbFromBlocks(VP.blocks).block.verb))*/ (complement /* && isStativeHelper(getVerbFromBlocks(VP.blocks).block.verb))*/
? ` ${complement}` ? ` ${complement}`
: "") : "") +
+ APs; APs
);
} }
const engSubj = getSubjectSelectionFromBlocks(VP.blocks).selection; const engSubj = getSubjectSelectionFromBlocks(VP.blocks).selection;
const obj = getObjectSelectionFromBlocks(VP.blocks).selection; const obj = getObjectSelectionFromBlocks(VP.blocks).selection;
const engObj = typeof obj === "object" const engObj =
typeof obj === "object"
? obj ? obj
: (obj === "none" || obj === T.Person.ThirdPlurMale) : obj === "none" || obj === T.Person.ThirdPlurMale
? "" ? ""
: undefined; : undefined;
const engAPs = getEngAPs(VP.blocks); const engAPs = getEngAPs(VP.blocks);
const engComplement = getEngComplement(VP.blocks); const engComplement = getEngComplement(VP.blocks);
// require all English parts for making the English phrase // require all English parts for making the English phrase
return (VP.englishBase && engSubj && engObj !== undefined) return VP.englishBase && engSubj && engObj !== undefined
? VP.englishBase.map(e => insertEWords(e, { ? VP.englishBase
.map((e) =>
insertEWords(e, {
// TODO: make sure we actually have the english // TODO: make sure we actually have the english
subject: getEnglishFromRendered(engSubj) || "", subject: getEnglishFromRendered(engSubj) || "",
object: engObj ? getEnglishFromRendered(engObj) : "", object: engObj ? getEnglishFromRendered(engObj) : "",
APs: engAPs, APs: engAPs,
complement: engComplement, complement: engComplement,
})).map(capitalizeFirstLetter) })
)
.map(capitalizeFirstLetter)
: undefined; : undefined;
} }
function compileEnglishEP(EP: T.EPRendered): string[] | 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; return e.replace("$SUBJ", subject).replace("$PRED", predicate || "") + APs;
} }
const engSubjR = getSubjectSelectionFromBlocks(EP.blocks).selection; const engSubjR = getSubjectSelectionFromBlocks(EP.blocks).selection;
@ -335,21 +463,26 @@ function compileEnglishEP(EP: T.EPRendered): string[] | undefined {
const engPred = getEnglishFromRendered(engPredR); const engPred = getEnglishFromRendered(engPredR);
const engAPs = getEngAPs(EP.blocks); const engAPs = getEngAPs(EP.blocks);
// require all English parts for making the English phrase // require all English parts for making the English phrase
const b = (EP.englishBase && engSubj && engPred) const b =
? EP.englishBase.map(e => insertEWords(e, { EP.englishBase && engSubj && engPred
? EP.englishBase.map((e) =>
insertEWords(e, {
subject: engSubj, subject: engSubj,
predicate: engPred, predicate: engPred,
APs: engAPs, APs: engAPs,
})) })
)
: undefined; : undefined;
return b?.map(capitalizeFirstLetter); 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 { function findDuplicateMiniP(mp: T.MiniPronoun[]): T.MiniPronoun | undefined {
const duplicates = mp.filter((item, index) => ( const duplicates = mp.filter(
mp.findIndex(m => item.ps.p === m.ps.p) !== index (item, index) => mp.findIndex((m) => item.ps.p === m.ps.p) !== index
)); );
if (duplicates.length === 0) return undefined; if (duplicates.length === 0) return undefined;
return duplicates[0]; return duplicates[0];
} }
@ -358,13 +491,15 @@ export function checkForMiniPronounsError(s: T.EPSelectionState | T.VPSelectionS
const EPS = completeEPSelection(s); const EPS = completeEPSelection(s);
if (!EPS) return undefined; if (!EPS) return undefined;
return renderEP(EPS).kids; return renderEP(EPS).kids;
}; }
const VPS = completeVPSelection(s); const VPS = completeVPSelection(s);
if (!VPS) return undefined; if (!VPS) return undefined;
return renderVP(VPS).kids; return renderVP(VPS).kids;
})(); })();
if (!kids) return undefined; 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) { if (miniPronouns.length > 2) {
return "can't add another mini-pronoun, there are alread two"; 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; 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 (NP === undefined) return [];
if (typeof NP !== "object") return []; if (typeof NP !== "object") return [];
if (!NP.selection.possesor) 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); return findPossesivesInNP(NP.selection.possesor.np);
} }
function findPossesivesInAdjectives(a: T.Rendered<T.AdjectiveSelection>[]): T.Rendered<T.NPSelection>[] { function findPossesivesInAdjectives(
return a.reduce((accum, curr): T.Rendered<T.NPSelection>[] => ([ a: T.Rendered<T.AdjectiveSelection>[]
): T.Rendered<T.NPSelection>[] {
return a.reduce(
(accum, curr): T.Rendered<T.NPSelection>[] => [
...accum, ...accum,
...findPossesivesInAdjective(curr), ...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 []; if (!a.sandwich) return [];
return findPossesivesInNP(a.sandwich.inside); 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) { 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;
} }
return [r];
}

View File

@ -1,24 +1,24 @@
import * as T from "../../../types"; import * as T from "../../../types";
import { import { mapVerbRenderedOutput } from "../fmaps";
mapVerbRenderedOutput,
} from "../fmaps";
import { removeAccents } from "../accent-helpers"; import { removeAccents } from "../accent-helpers";
import { import { getPersonFromNP, isPastTense } from "./vp-tools";
getPersonFromNP, import { isImperativeTense, isPattern4Entry } from "../type-predicates";
isPastTense,
} from "./vp-tools";
import {
isImperativeTense,
isPattern4Entry,
} from "../type-predicates";
import { renderVerb } from "../new-verb-engine/render-verb"; import { renderVerb } from "../new-verb-engine/render-verb";
import { renderEnglishVPBase } from "./english-vp-rendering"; import { renderEnglishVPBase } from "./english-vp-rendering";
import { renderNPSelection } from "./render-np"; 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 { renderAPSelection } from "./render-ap";
import { findPossesivesToShrink, orderKids, getMiniPronounPs } from "./render-common"; import {
findPossesivesToShrink,
orderKids,
getMiniPronounPs,
} from "./render-common";
import { renderComplementSelection } from "./render-complement"; import { renderComplementSelection } from "./render-complement";
import { personToGenNum } from "../misc-helpers";
export function renderVP(VP: T.VPSelectionComplete): T.VPRendered { export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
const subject = getSubjectSelection(VP.blocks).selection; 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 isPast = isPastTense(VP.verb.tense);
const isTransitive = object !== "none"; const isTransitive = object !== "none";
const { king, servant } = getKingAndServant(isPast, isTransitive); const { king, servant } = getKingAndServant(isPast, isTransitive);
const kingPerson = getPersonFromNP( const kingPerson = getPersonFromNP(king === "subject" ? subject : object);
king === "subject" ? subject : object, const complementPerson = getPersonFromNP(object ? object : subject);
);
const complementPerson = getPersonFromNP(object ? object : subject)
// TODO: more elegant way of handling this type safety // TODO: more elegant way of handling this type safety
if (kingPerson === undefined) { if (kingPerson === undefined) {
throw new Error("king of sentance does not exist"); throw new Error("king of sentance does not exist");
} }
const subjectPerson = getPersonFromNP(subject); const subjectPerson = getPersonFromNP(subject);
const objectPerson = getPersonFromNP(object); const objectPerson = getPersonFromNP(object);
const inflectSubject = isPast && isTransitive && !isMascSingAnimatePattern4(subject); const inflectSubject =
isPast && isTransitive && !isMascSingAnimatePattern4(subject);
const inflectObject = !isPast && isFirstOrSecondPersPronoun(object); const inflectObject = !isPast && isFirstOrSecondPersPronoun(object);
// Render Elements // Render Elements
const firstBlocks = renderVPBlocks(VP.blocks, VP.externalComplement, { const firstBlocks = renderVPBlocks(VP.blocks, VP.externalComplement, {
@ -49,12 +48,16 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
const { vbs, hasBa } = renderVerb({ const { vbs, hasBa } = renderVerb({
verb: VP.verb.verb, verb: VP.verb.verb,
tense: VP.verb.tense, tense: VP.verb.tense,
person: kingPerson, subject: subjectPerson,
complementGenNum: personToGenNum(objectPerson || kingPerson), object: objectPerson,
voice: VP.verb.voice, voice: VP.verb.voice,
negative: VP.verb.negative, 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 // just enter the negative in the verb blocks
return { return {
type: "VPRendered", type: "VPRendered",
@ -63,7 +66,7 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
isPast, isPast,
isTransitive, isTransitive,
isCompound: VP.verb.isCompound, isCompound: VP.verb.isCompound,
blocks: VBwNeg.map(VBvars => [...firstBlocks, ...VBvars]), blocks: VBwNeg.map((VBvars) => [...firstBlocks, ...VBvars]),
kids: getVPKids(hasBa, VP.blocks, VP.form, king), kids: getVPKids(hasBa, VP.blocks, VP.form, king),
englishBase: renderEnglishVPBase({ englishBase: renderEnglishVPBase({
subjectPerson, 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 subject = getSubjectSelection(blocks).selection;
const objectS = getObjectSelection(blocks).selection; const objectS = getObjectSelection(blocks).selection;
const object = typeof objectS === "object" ? objectS : undefined; const object = typeof objectS === "object" ? objectS : undefined;
const servantNP = king === "subject" ? object : subject; const servantNP = king === "subject" ? object : subject;
const shrunkenServant = (form.shrinkServant && servantNP) const shrunkenServant =
form.shrinkServant && servantNP
? makeKid(shrinkServant(servantNP)) ? makeKid(shrinkServant(servantNP))
: undefined; : undefined;
const shrunkenPossesives = findPossesivesToShrink(removeAbbreviated(blocks, form, king)).map(makeKid); const shrunkenPossesives = findPossesivesToShrink(
removeAbbreviated(blocks, form, king)
).map(makeKid);
return orderKids([ return orderKids([
...hasBa ? [makeKid({ type: "ba" })] : [], ...(hasBa ? [makeKid({ type: "ba" })] : []),
...shrunkenServant ? [shrunkenServant] : [], ...(shrunkenServant ? [shrunkenServant] : []),
...shrunkenPossesives ? shrunkenPossesives : [], ...(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 }) => { return blocks.filter(({ block }) => {
if (block.type === "subjectSelection") { if (block.type === "subjectSelection") {
if (form.shrinkServant && king === "object") return false; 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) { if (!negative) {
return [blocks.flat().map(makeBlock)]; return [blocks.flat().map(makeBlock)];
}; }
const blocksA = 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 neg = makeBlock({ type: "negative", imperative });
const nonStandPerfectiveSplit = hasNonStandardPerfectiveSplit(blocks); const nonStandPerfectiveSplit = hasNonStandardPerfectiveSplit(blocks);
if (blocks[1].length === 2) { 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), neg, 2),
insertFromEnd(swapEndingBlocks(blocksA, 2), neg, 3), insertFromEnd(swapEndingBlocks(blocksA, 2), neg, 3),
insertFromEnd(blocksNoAccentA, neg, 1), insertFromEnd(blocksNoAccentA, neg, 1),
] ];
} }
return [ return [
insertFromEnd(swapEndingBlocks(blocksA), neg, 2), insertFromEnd(swapEndingBlocks(blocksA), neg, 2),
@ -149,11 +170,7 @@ function insertFromEnd<X>(arr: X[], x: X, n: number): X[] {
if (n === 0) { if (n === 0) {
return [...arr, x]; return [...arr, x];
} }
return [ return [...arr.slice(0, arr.length - n), x, ...arr.slice(-n)];
...arr.slice(0, arr.length - n),
x,
...arr.slice(-n),
];
} }
function hasNonStandardPerfectiveSplit([[ph]]: T.VerbRenderedOutput): boolean { function hasNonStandardPerfectiveSplit([[ph]]: T.VerbRenderedOutput): boolean {
@ -177,26 +194,39 @@ function shrinkServant(np: T.NPSelection): T.MiniPronoun {
}; };
} }
function renderVPBlocks(
function renderVPBlocks(blocks: T.VPSBlockComplete[], externalComplement: T.VPSelectionComplete["externalComplement"], config: { blocks: T.VPSBlockComplete[],
inflectSubject: boolean, externalComplement: T.VPSelectionComplete["externalComplement"],
inflectObject: boolean, config: {
king: "subject" | "object", inflectSubject: boolean;
complementPerson: T.Person | undefined, inflectObject: boolean;
}): T.Block[] { king: "subject" | "object";
complementPerson: T.Person | undefined;
}
): T.Block[] {
const object = getObjectSelection(blocks); const object = getObjectSelection(blocks);
const subject = getSubjectSelection(blocks); const subject = getSubjectSelection(blocks);
const adverbPerson = typeof object.selection === "object" const adverbPerson =
typeof object.selection === "object"
? getPersonFromNP(object.selection) ? getPersonFromNP(object.selection)
: getPersonFromNP(subject.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[] => { return b.reduce((blocks, { block }): T.Block[] => {
if (block.type === "subjectSelection") { if (block.type === "subjectSelection") {
return [ return [
...blocks, ...blocks,
makeBlock({ makeBlock({
type: "subjectSelection", 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 [ return [
...blocks, ...blocks,
makeBlock({ makeBlock({
@ -232,43 +269,58 @@ function renderVPBlocks(blocks: T.VPSBlockComplete[], externalComplement: T.VPSe
renderComplementSelection( renderComplementSelection(
block, block,
// just for typesafety // TODO: only include the person if we're doing an adjective // 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[]); }, [] as T.Block[]);
} }
function whatsAdjustable(VP: T.VPSelectionComplete): "both" | "king" | "servant" { function whatsAdjustable(
VP: T.VPSelectionComplete
): "both" | "king" | "servant" {
// TODO: intransitive dynamic compounds? // TODO: intransitive dynamic compounds?
return (VP.verb.isCompound === "dynamic" && VP.verb.transitivity === "transitive") return VP.verb.isCompound === "dynamic" &&
? (isPastTense(VP.verb.tense) ? "servant" : "king") VP.verb.transitivity === "transitive"
? isPastTense(VP.verb.tense)
? "servant"
: "king"
: VP.verb.transitivity === "transitive" : VP.verb.transitivity === "transitive"
? (VP.verb.voice === "active" ? "both" : "king") ? VP.verb.voice === "active"
? "both"
: "king"
: VP.verb.transitivity === "intransitive" : VP.verb.transitivity === "intransitive"
? "king" ? "king"
// grammTrans : // grammTrans
: isPastTense(VP.verb.tense) isPastTense(VP.verb.tense)
? "servant" ? "servant"
: "king"; : "king";
} }
export function getKingAndServant(isPast: boolean, isTransitive: boolean): export function getKingAndServant(
{ king: "subject", servant: "object" } | isPast: boolean,
{ king: "object", servant: "subject" } | isTransitive: boolean
{ king: "subject", servant: undefined } { ):
| { king: "subject"; servant: "object" }
| { king: "object"; servant: "subject" }
| { king: "subject"; servant: undefined } {
if (!isTransitive) { if (!isTransitive) {
return { king: "subject", servant: undefined }; return { king: "subject", servant: undefined };
} }
return isPast ? { return isPast
? {
king: "object", king: "object",
servant: "subject", servant: "subject",
} : { }
: {
king: "subject", king: "subject",
servant: "object", 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 (typeof o !== "object") return false;
if (o.selection.type !== "pronoun") return false; if (o.selection.type !== "pronoun") return false;
return [0, 1, 2, 3, 6, 7, 8, 9].includes(o.selection.person); 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") { if (np.selection.type !== "noun") {
return false; return false;
} }
return isPattern4Entry(np.selection.entry) return (
&& np.selection.entry.c.includes("anim.") isPattern4Entry(np.selection.entry) &&
&& (np.selection.number === "singular") np.selection.entry.c.includes("anim.") &&
&& (np.selection.gender === "masc"); np.selection.number === "singular" &&
np.selection.gender === "masc"
);
} }

View File

@ -1,13 +1,15 @@
import * as T from "../../../types"; import * as T from "../../../types";
import { import { concatPsString, psRemove, psStringEquals } from "../p-text-helpers";
concatPsString, import { isPerfectTense } from "../type-predicates";
psRemove,
psStringEquals,
} from "../p-text-helpers";
import { isImperativeTense, isPerfectTense } from "../type-predicates";
import * as grammarUnits from "../grammar-units"; import * as grammarUnits from "../grammar-units";
import { isSecondPerson, randomNumber } from "../misc-helpers"; 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 { export function isInvalidSubjObjCombo(subj: T.Person, obj: T.Person): boolean {
const firstPeople = [ const firstPeople = [
@ -23,116 +25,120 @@ export function isInvalidSubjObjCombo(subj: T.Person, obj: T.Person): boolean {
T.Person.SecondPlurFemale, T.Person.SecondPlurFemale,
]; ];
return ( return (
(firstPeople.includes(subj) && firstPeople.includes(obj)) (firstPeople.includes(subj) && firstPeople.includes(obj)) ||
||
(secondPeople.includes(subj) && secondPeople.includes(obj)) (secondPeople.includes(subj) && secondPeople.includes(obj))
); );
} }
/** // DEPRECATED
* @param conjR // /**
* @param tense // * @param conjR
* @param voice // * @param tense
* @param negative // * @param voice
* @returns // * @param negative
*/ // * @returns
export function getTenseVerbForm( // */
conjR: T.VerbConjugation, // export function getTenseVerbForm(
tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense, // conjR: T.VerbConjugation,
voice: "active" | "passive", // tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense,
mode: "charts" | "phrase-building", // voice: "active" | "passive",
negative: boolean, // mode: "charts" | "phrase-building",
): T.VerbForm | T.ImperativeForm { // negative: boolean,
const conj = (voice === "passive" && conjR.passive) ? conjR.passive : conjR; // ): T.VerbForm | T.ImperativeForm {
if (isImperativeTense(tense)) { // const conj = (voice === "passive" && conjR.passive) ? conjR.passive : conjR;
const impPassError = new Error("can't use imperative tenses with passive voice") // if (isImperativeTense(tense)) {
if (voice === "passive") { // const impPassError = new Error("can't use imperative tenses with passive voice")
throw impPassError; // if (voice === "passive") {
} // throw impPassError;
if (!conj.imperfective.imperative || !conj.perfective.imperative) throw impPassError; // }
// charts can't display negative form // if (!conj.imperfective.imperative || !conj.perfective.imperative) throw impPassError;
return (tense === "perfectiveImperative" && (!negative || mode === "charts")) // // charts can't display negative form
? conj.perfective.imperative // return (tense === "perfectiveImperative" && (!negative || mode === "charts"))
: conj.imperfective.imperative; // ? conj.perfective.imperative
} // : conj.imperfective.imperative;
if (tense === "presentVerb") { // }
return conj.imperfective.nonImperative; // if (tense === "presentVerb") {
} // return conj.imperfective.nonImperative;
if (tense === "subjunctiveVerb") { // }
return conj.perfective.nonImperative; // if (tense === "subjunctiveVerb") {
} // return conj.perfective.nonImperative;
if (tense === "imperfectiveFuture") { // }
return conj.imperfective.future; // if (tense === "imperfectiveFuture") {
} // return conj.imperfective.future;
if (tense === "perfectiveFuture") { // }
return conj.perfective.future; // if (tense === "perfectiveFuture") {
} // return conj.perfective.future;
if (tense === "imperfectivePast") { // }
return conj.imperfective.past; // if (tense === "imperfectivePast") {
} // return conj.imperfective.past;
if (tense === "perfectivePast") { // }
return conj.perfective.past; // if (tense === "perfectivePast") {
} // return conj.perfective.past;
if (tense === "habitualImperfectivePast") { // }
return conj.imperfective.habitualPast; // if (tense === "habitualImperfectivePast") {
} // return conj.imperfective.habitualPast;
if (tense === "habitualPerfectivePast") { // }
return conj.perfective.habitualPast; // if (tense === "habitualPerfectivePast") {
} // return conj.perfective.habitualPast;
if (tense === "presentVerbModal") { // }
return conj.imperfective.modal.nonImperative; // if (tense === "presentVerbModal") {
} // return conj.imperfective.modal.nonImperative;
if (tense === "subjunctiveVerbModal") { // }
return conj.perfective.modal.nonImperative; // if (tense === "subjunctiveVerbModal") {
} // return conj.perfective.modal.nonImperative;
if (tense === "imperfectiveFutureModal") { // }
return conj.imperfective.modal.future; // if (tense === "imperfectiveFutureModal") {
} // return conj.imperfective.modal.future;
if (tense === "perfectiveFutureModal") { // }
return conj.perfective.modal.future; // if (tense === "perfectiveFutureModal") {
} // return conj.perfective.modal.future;
if (tense === "imperfectivePastModal") { // }
return conj.imperfective.modal.past; // if (tense === "imperfectivePastModal") {
} // return conj.imperfective.modal.past;
if (tense === "perfectivePastModal") { // }
return conj.perfective.modal.past; // if (tense === "perfectivePastModal") {
} // return conj.perfective.modal.past;
if (tense === "habitualImperfectivePastModal") { // }
return conj.imperfective.modal.habitualPast; // if (tense === "habitualImperfectivePastModal") {
} // return conj.imperfective.modal.habitualPast;
if (tense === "habitualPerfectivePastModal") { // }
return conj.perfective.modal.habitualPast; // if (tense === "habitualPerfectivePastModal") {
} // return conj.perfective.modal.habitualPast;
if (tense === "presentPerfect") { // }
return conj.perfect.present; // if (tense === "presentPerfect") {
} // return conj.perfect.present;
if (tense === "pastPerfect") { // }
return conj.perfect.past; // if (tense === "pastPerfect") {
} // return conj.perfect.past;
if (tense === "futurePerfect") { // }
return conj.perfect.future; // if (tense === "futurePerfect") {
} // return conj.perfect.future;
if (tense === "habitualPerfect") { // }
return conj.perfect.habitual; // if (tense === "habitualPerfect") {
} // return conj.perfect.habitual;
if (tense === "subjunctivePerfect") { // }
return conj.perfect.subjunctive; // if (tense === "subjunctivePerfect") {
} // return conj.perfect.subjunctive;
if (tense === "wouldBePerfect") { // }
return conj.perfect.wouldBe; // if (tense === "wouldBePerfect") {
} // return conj.perfect.wouldBe;
if (tense === "pastSubjunctivePerfect") { // }
return conj.perfect.pastSubjunctive; // if (tense === "pastSubjunctivePerfect") {
} // return conj.perfect.pastSubjunctive;
if (tense === "wouldHaveBeenPerfect") { // }
return conj.perfect.wouldHaveBeen; // if (tense === "wouldHaveBeenPerfect") {
} // return conj.perfect.wouldHaveBeen;
throw new Error("unknown tense"); // }
} // throw new Error("unknown tense");
// }
export function getPersonFromNP(np: T.NPSelection): T.Person; export function getPersonFromNP(np: T.NPSelection): T.Person;
export function getPersonFromNP(np: T.NPSelection | T.ObjectNP): T.Person | undefined; export function getPersonFromNP(
export function getPersonFromNP(np: T.NPSelection | T.ObjectNP): T.Person | undefined { np: T.NPSelection | T.ObjectNP
): T.Person | undefined;
export function getPersonFromNP(
np: T.NPSelection | T.ObjectNP
): T.Person | undefined {
if (np === "none") { if (np === "none") {
return undefined; return undefined;
} }
@ -144,15 +150,21 @@ export function getPersonFromNP(np: T.NPSelection | T.ObjectNP): T.Person | unde
return np.selection.person; return np.selection.person;
} }
return np.selection.number === "plural" return np.selection.number === "plural"
? (np.selection.gender === "masc" ? T.Person.ThirdPlurMale : T.Person.ThirdPlurFemale) ? np.selection.gender === "masc"
: (np.selection.gender === "masc" ? T.Person.ThirdSingMale : T.Person.ThirdSingFemale); ? T.Person.ThirdPlurMale
: T.Person.ThirdPlurFemale
: np.selection.gender === "masc"
? T.Person.ThirdSingMale
: T.Person.ThirdSingFemale;
} }
export function removeBa(ps: T.PsString): T.PsString { export function removeBa(ps: T.PsString): T.PsString {
return psRemove(ps, concatPsString(grammarUnits.baParticle, " ")); return psRemove(ps, concatPsString(grammarUnits.baParticle, " "));
} }
export function getTenseFromVerbSelection(vs: T.VerbSelection): T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense { export function getTenseFromVerbSelection(
vs: T.VerbSelection
): T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense {
function verbTenseToModalTense(tn: T.VerbTense): T.AbilityTense { function verbTenseToModalTense(tn: T.VerbTense): T.AbilityTense {
if (tn === "presentVerb") { if (tn === "presentVerb") {
return "presentVerbModal"; return "presentVerbModal";
@ -208,25 +220,29 @@ export function perfectTenseHasBa(tense: T.PerfectTense): boolean {
} }
export function removeDuplicates(psv: T.PsString[]): T.PsString[] { export function removeDuplicates(psv: T.PsString[]): T.PsString[] {
return psv.filter((ps, i, arr) => ( return psv.filter(
i === arr.findIndex(t => ( (ps, i, arr) => i === arr.findIndex((t) => psStringEquals(t, ps))
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 subject = getSubjectSelection(vps.blocks).selection;
const object = getObjectSelection(vps.blocks).selection; const object = getObjectSelection(vps.blocks).selection;
if ("tenseCategory" in vps.verb) { 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;
} }
return { return {
...vps, ...vps,
blocks: adjustObjectSelection( blocks: adjustObjectSelection(
adjustSubjectSelection(vps.blocks, object), adjustSubjectSelection(vps.blocks, object),
subject, subject
), ),
}; };
} }
@ -237,12 +253,14 @@ export function switchSubjObj<V extends T.VPSelectionState | T.VPSelectionComple
...vps, ...vps,
blocks: adjustObjectSelection( blocks: adjustObjectSelection(
adjustSubjectSelection(vps.blocks, object), 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)) { if (!VPSBlocksAreComplete(vps.blocks)) {
return undefined; 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 subject = getSubjectSelection(vps.blocks).selection;
const object = getObjectSelection(vps.blocks).selection; const object = getObjectSelection(vps.blocks).selection;
const subjIs2ndPerson = (subject?.selection.type === "pronoun") && isSecondPerson(subject.selection.person); const subjIs2ndPerson =
const objIs2ndPerson = (typeof object === "object") subject?.selection.type === "pronoun" &&
&& (object.selection.type === "pronoun") isSecondPerson(subject.selection.person);
&& isSecondPerson(object.selection.person); const objIs2ndPerson =
typeof object === "object" &&
object.selection.type === "pronoun" &&
isSecondPerson(object.selection.person);
const default2ndPersSubject: T.NPSelection = { const default2ndPersSubject: T.NPSelection = {
type: "NP", type: "NP",
selection: { selection: {
@ -311,7 +334,7 @@ export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState):
if (typeof object !== "object" || object.selection.type !== "pronoun") { if (typeof object !== "object" || object.selection.type !== "pronoun") {
return { return {
...vps, ...vps,
blocks: adjustSubjectSelection(vps.blocks, default2ndPersSubject) blocks: adjustSubjectSelection(vps.blocks, default2ndPersSubject),
}; };
} }
return { return {
@ -324,7 +347,7 @@ export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState):
...object.selection, ...object.selection,
person: getNon2ndPersPronoun(), 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" resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz"
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== 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: fragment-cache@^0.2.1:
version "0.2.1" version "0.2.1"
resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz" 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" resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== 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: react-overlays@^5.1.1:
version "5.1.1" version "5.1.1"
resolved "https://registry.npmjs.org/react-overlays/-/react-overlays-5.1.1.tgz" resolved "https://registry.npmjs.org/react-overlays/-/react-overlays-5.1.1.tgz"