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

@ -9,50 +9,64 @@
import * as T from "../../types"; import * as T from "../../types";
const persInfs: { const persInfs: {
label: string; label: string;
value: T.PersonInflectionsField; value: T.PersonInflectionsField;
}[] = [ }[] = [
{ {
label: "masc. sing.", label: "masc. sing.",
value: "mascSing", value: "mascSing",
}, },
{ {
label: "fem. sing.", label: "fem. sing.",
value: "femSing", value: "femSing",
}, },
{ {
label: "masc. plur.", label: "masc. plur.",
value: "mascPlur", value: "mascPlur",
}, },
{ {
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 className="mr-2"> <div
When the <strong>{props.transitivity === "intransitive" ? "subject" : "object"}</strong> is style={{
</div> display: "flex",
<div> flexDirection: "row",
<select className="form-control form-control-sm d-inline-block" value={props.persInf} id="verb_info_pers_select" onChange={hChange}> alignItems: "center",
{persInfs.map((pers) => ( justifyContent: "center",
<option key={pers.value} value={pers.value}> }}
{pers.label} >
</option> <div className="mr-2">
))} When the <strong>{props.subjOrObj}</strong> is
</select> </div>
</div> <div>
</div>; <select
className="form-control form-control-sm d-inline-block"
value={props.persInf}
id="verb_info_pers_select"
onChange={hChange}
>
{persInfs.map((pers) => (
<option key={pers.value} value={pers.value}>
{pers.label}
</option>
))}
</select>
</div>
</div>
);
} }
export default PersInfsPicker; export default PersInfsPicker;

View File

@ -3,50 +3,79 @@ 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]);
function advanceVersion() { function advanceVersion() {
setVersion((version + 1) % item.length); setVersion((version + 1) % item.length);
} }
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
display: "flex", style={{
flexDirection: "row", display: "flex",
flexWrap: "wrap", flexDirection: "row",
justifyContent: center ? "center" : "space-between", flexWrap: "wrap",
alignItems: "center", justifyContent: center ? "center" : "space-between",
}}> alignItems: "center",
<div> }}
<div> >
<Pashto opts={textOptions}>{w}</Pashto> <div>
</div> <div>
<div> <Pashto opts={textOptions}>{w}</Pashto>
<Phonetics opts={textOptions}>{w}</Phonetics> </div>
</div> <div>
{w.e && (Array.isArray(w.e) <Phonetics opts={textOptions}>{w}</Phonetics>
? w.e.map(e => <div key={e} className="text-muted small">{e}</div>) </div>
: <div className="text-muted">{w.e}</div>)} {w.e &&
(Array.isArray(w.e) ? (
w.e.map((e) => (
<div key={e} className="text-muted small">
{e}
</div> </div>
{item.length > 1 && ))
<button className="btn btn-sm btn-light mx-2 my-2" onClick={advanceVersion}> ) : (
ver. {version + 1}/{item.length} {arrowDown} <div className="text-muted">{w.e}</div>
</button> ))}
} </div>
</div> {item.length > 1 && (
</td> <button
); className="btn btn-sm btn-light mx-2 my-2"
onClick={advanceVersion}
>
ver. {version + 1}/{item.length} {arrowDown}
</button>
)}
</div>
</td>
);
} }
export default TableCell; export default TableCell;

View File

@ -13,107 +13,157 @@ import SingleItemDisplay from "./SingleItemDisplay";
import ButtonSelect from "./ButtonSelect"; import ButtonSelect from "./ButtonSelect";
import VerbTable from "./VerbTable"; import VerbTable from "./VerbTable";
import { 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(
if (!displayForm.past) { info: T.NonComboVerbInfo,
return null; displayForm: T.DisplayForm
} ): React.ReactNode {
const beginning = "Verb agrees with the "; if (!displayForm.past) {
const agreesWith = (info.transitivity !== "intransitive" && displayForm.past && !displayForm.passive) return null;
? "object" }
: "subject"; const beginning = "Verb agrees with the ";
const extraExplanation = (!displayForm.past) const agreesWith =
? "" info.transitivity !== "intransitive" &&
: (info.transitivity === "grammatically transitive") displayForm.past &&
? " which is an unwritten 3rd pers. masc. object." !displayForm.passive
: (info.type === "generative stative compound" || info.type === "dynamic compound") ? "object"
? ` which is the complement ${info.objComplement.plural ? info.objComplement.plural.p : info.objComplement.entry.p} (${getEnglishPersonInfo(info.objComplement.person)})` : "subject";
: "" const extraExplanation = !displayForm.past
return <><strong>Note:</strong> {beginning}<strong>{agreesWith}</strong>{extraExplanation}</> ? ""
: info.transitivity === "grammatically transitive"
? " which is an unwritten 3rd pers. masc. object."
: info.type === "generative stative compound" ||
info.type === "dynamic compound"
? ` which is the complement ${
info.objComplement.plural
? info.objComplement.plural.p
: info.objComplement.entry.p
} (${getEnglishPersonInfo(info.objComplement.person)})`
: "";
return (
<>
<strong>Note:</strong> {beginning}
<strong>{agreesWith}</strong>
{extraExplanation}
</>
);
} }
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(() => { if (length === "mini" && !("mini" in chosenPersInf)) {
if (length === "mini" && !("mini" in chosenPersInf)) { setLength(defaultLength);
setLength(defaultLength); }
} // setPersInf("mascSing");
// setPersInf("mascSing"); // setShowingExplanation(false);
// setShowingExplanation(false); }, [block, length, chosenPersInf, defaultLength]);
}, [block, length, chosenPersInf, defaultLength]); // TODO: This could be handled better to avoid the react-hooks/exhaustive-deps warning ?
// TODO: This could be handled better to avoid the react-hooks/exhaustive-deps warning ? useEffect(() => {
useEffect(() => { setShowingExplanation(false);
setShowingExplanation(false); }, [block]);
}, [block]); const hasVariations =
const hasVariations = (!("masc" in form)) && (!("p" in form)) && (!isSentenceForm(form)) && !isAllOne(form as T.VerbBlock | T.ImperativeBlock); !("masc" in form) &&
return <> !("p" in form) &&
{(("label" in displayForm && info) && showingFormInfo) && <> !isSentenceForm(form) &&
{(hasVariations || displayForm.past) && <p className="small text-muted"> !isAllOne(form as T.VerbBlock | T.ImperativeBlock);
{agreementInfo(info, displayForm)} return (
</p>} <>
<div className="mb-1 d-flex justify-content-between align-items-center"> {"label" in displayForm && info && showingFormInfo && (
<samp>Formula: {displayForm.formula}</samp> <>
<button className="btn btn-sm btn-outline-secondary text-nowrap ml-2" onClick={() => setShowingExplanation(!showingExplanation)}> {(hasVariations || displayForm.past) && (
<i className={`fas fa-caret-${showingExplanation ? "down" : "right"}`} /> Meaning <p className="small text-muted">
</button> {agreementInfo(info, displayForm)}
</div> </p>
{showingExplanation && <div className="my-2"> )}
{displayForm.explanation} <div className="mb-1 d-flex justify-content-between align-items-center">
</div>} <samp>Formula: {displayForm.formula}</samp>
</>} <button
{"long" in chosenPersInf && className="btn btn-sm btn-outline-secondary text-nowrap ml-2"
<div className="text-center"> onClick={() => setShowingExplanation(!showingExplanation)}
<ButtonSelect >
small <i
options={[ className={`fas fa-caret-${
{ label: "Long", value: "long" }, showingExplanation ? "down" : "right"
{ label: "Short", value: "short" }, }`}
..."mini" in chosenPersInf ? [{ />{" "}
label: "Mini", value: "mini", Meaning
}] : [], </button>
]} </div>
value={length} {showingExplanation && (
handleChange={(p) => setLength(p as T.Length)} <div className="my-2">{displayForm.explanation}</div>
/> )}
</div> </>
} )}
{("mascSing" in block && info) && <PersonInfsPicker {"long" in chosenPersInf && (
persInf={persInf} <div className="text-center">
handleChange={(p) => setPersInf(p)} <ButtonSelect
transitivity={info.transitivity} small
/>} options={[
{"masc" in form ? { label: "Long", value: "long" },
<InflectionsTable inf={form} textOptions={textOptions} /> { label: "Short", value: "short" },
: "p" in form ? ...("mini" in chosenPersInf
<SingleItemDisplay item={form} english={english} textOptions={textOptions} /> ? [
: {
<VerbTable block={form} english={english} textOptions={textOptions} /> label: "Mini",
} value: "mini",
},
]
: []),
]}
value={length}
handleChange={(p) => setLength(p as T.Length)}
/>
</div>
)}
{"mascSing" in block && info && (
<PersonInfsPicker
persInf={persInf}
handleChange={(p) => setPersInf(p)}
subjOrObj={info.transitivity === "transitive" ? "object" : "subject"}
/>
)}
{"masc" in form ? (
<InflectionsTable inf={form} textOptions={textOptions} />
) : "p" in form ? (
<SingleItemDisplay
item={form}
english={english}
textOptions={textOptions}
/>
) : (
<VerbTable block={form} english={english} textOptions={textOptions} />
)}
</> </>
);
} }
export default VerbFormDisplay; export default VerbFormDisplay;

View File

@ -6,14 +6,11 @@
* *
*/ */
import { CSSProperties, useState } from "react";
import { import {
CSSProperties, pickPersInf,
useState, hasPersInfs,
} from "react"; noPersInfs,
import {
pickPersInf,
hasPersInfs,
noPersInfs,
} from "../../../lib/src/misc-helpers"; } from "../../../lib/src/misc-helpers";
import VerbInfoItemDisplay from "./VerbInfoItemDisplay"; import VerbInfoItemDisplay from "./VerbInfoItemDisplay";
import PersonInfsPicker from "../PersInfsPicker"; import PersonInfsPicker from "../PersInfsPicker";
@ -26,56 +23,75 @@ import fadedTree from "./faded-tree.svg";
// import video from "../icons/camera-video-fill"; // import video from "../icons/camera-video-fill";
const indentR = { const indentR = {
paddingLeft: "1rem", paddingLeft: "1rem",
}; };
const highlight = { const highlight = {
background: "rgba(255, 227, 10, 0.6)", background: "rgba(255, 227, 10, 0.6)",
}; };
const title: CSSProperties = { const title: CSSProperties = {
fontWeight: "bolder", fontWeight: "bolder",
marginBottom: "0.5rem", marginBottom: "0.5rem",
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 &&
const showPersInf = hasPersInfs(info); "perfectiveSplit" in info.stem &&
const [persInf, setPersInf] = useState<T.PersonInflectionsField>("mascSing"); !!(info.root.perfectiveSplit || info.stem.perfectiveSplit);
const [split, setSplit] = useState<boolean>(false); const showPersInf = hasPersInfs(info);
const perfectiveStem = (info.stem.perfectiveSplit && split) const [persInf, setPersInf] = useState<T.PersonInflectionsField>("mascSing");
? info.stem.perfectiveSplit const [split, setSplit] = useState<boolean>(false);
: info.stem.perfective; const perfectiveStem =
const perfectiveRoot = (info.root.perfectiveSplit && split) info.stem.perfectiveSplit && split
? info.root.perfectiveSplit ? info.stem.perfectiveSplit
: info.root.perfective; : info.stem.perfective;
const colClass = "col col-md-5 px-0 mb-1"; const perfectiveRoot =
const rowClass = "row justify-content-between"; info.root.perfectiveSplit && split
return ( ? info.root.perfectiveSplit
<div className="mt-3"> : info.root.perfective;
{showPersInf && <PersonInfsPicker const colClass = "col col-md-5 px-0 mb-1";
persInf={persInf} const rowClass = "row justify-content-between";
handleChange={(p) => setPersInf(p)} return (
transitivity={"transitivity" in info ? info.transitivity : "intransitive"} <div className="mt-3">
/>} {showPersInf && (
<div className="verb-info" style={{ <PersonInfsPicker
textAlign: "center", persInf={persInf}
maxWidth: "500px", handleChange={(p) => setPersInf(p)}
margin: "0 auto", subjOrObj={
backgroundImage: `url(${fadedTree})`, "transitivity" in info && info.transitivity === "intransitive"
backgroundRepeat: "no-repeat", ? "subject"
backgroundPosition: hidePastParticiple ? "50% 45%" : "50% 35%", : "object"
backgroundSize: "50%", }
}}> />
{/* <div style={{ )}
<div
className="verb-info"
style={{
textAlign: "center",
maxWidth: "500px",
margin: "0 auto",
backgroundImage: `url(${fadedTree})`,
backgroundRepeat: "no-repeat",
backgroundPosition: hidePastParticiple ? "50% 45%" : "50% 35%",
backgroundSize: "50%",
}}
>
{/* <div style={{
fontSize: "larger", fontSize: "larger",
}}> }}>
{info.def} {info.def}
@ -85,127 +101,158 @@ export function RootsAndStems({ textOptions, info, hidePastParticiple, highlight
{info.subDef} {info.subDef}
</div> </div>
} */} } */}
<div style={{ <div
border: "2px solid black", style={{
padding: "1rem", border: "2px solid black",
margin: "0.25rem", padding: "1rem",
}} className="container"> margin: "0.25rem",
<div className={rowClass + " align-items-center"}> }}
<div className={colClass}> className="container"
<i className="fas fa-video fa-lg" /> >
</div> <div className={rowClass + " align-items-center"}>
<div className={colClass}> <div className={colClass}>
<div className="d-flex flex-row justify-content-center align-items-center"> <i className="fas fa-video fa-lg" />
<div>
<i className="fas fa-camera fa-lg mx-3" />
</div>
{hasPerfectiveSplit && <div>
<button
className="btn btn-sm btn-outline-secondary"
onClick={() => setSplit(!split)}
>
{split ? "join" : "split"} head
</button>
</div>}
</div>
</div>
</div>
<div className={rowClass}>
<div className={colClass} style={highlighted?.includes("imperfective stem") ? highlight : {}}>
<div style={title as any}>
<div>Imperfective Stem</div>
</div>
<div style={indentR}>
<VerbInfoItemDisplay
item={pickPersInf(info.stem.imperfective, persInf)}
textOptions={textOptions}
tails={!noTails}
/>
</div>
</div>
<div className={colClass} style={highlighted?.includes("perfective stem") ? highlight : {}}>
<div style={title as any}>
<div>Perfective Stem</div>
</div>
<div style={indentR}>
<VerbInfoItemDisplay
item={pickPersInf(perfectiveStem, persInf)}
textOptions={textOptions}
tails={!noTails}
/>
</div>
</div>
</div>
<div className={rowClass}>
<div className={colClass} style={highlighted?.includes("imperfective root") ? highlight : {}}>
<div style={title as any}>
<div>Imperfective Root</div>
</div>
<div style={indentR}>
<VerbInfoItemDisplay
item={pickPersInf(info.root.imperfective, persInf)}
textOptions={textOptions}
/>
</div>
</div>
<div className={colClass} style={highlighted?.includes("perfective root") ? highlight : {}}>
<div>
<div style={title as any}>
<div>Perfective Root</div>
</div>
<div style={indentR}>
<VerbInfoItemDisplay
item={pickPersInf(perfectiveRoot, persInf)}
textOptions={textOptions}
/>
</div>
</div>
</div>
</div>
{!hidePastParticiple && "participle" in info &&<div className="text-center" style={highlighted?.includes("past participle") ? highlight : {}}>
<div style={title as any}>Past Participle</div>
<VerbInfoItemDisplay
item={pickPersInf(info.participle.past, persInf)}
textOptions={textOptions}
/>
</div>}
</div>
</div> </div>
</div> <div className={colClass}>
); <div className="d-flex flex-row justify-content-center align-items-center">
} <div>
<i className="fas fa-camera fa-lg mx-3" />
function VerbInfo({ info, textOptions, showingStemsAndRoots, toggleShowingSar, highlightInRootsAndStems, hidePastParticiple, hideTypeInfo }: { </div>
info: T.NonComboVerbInfo, {hasPerfectiveSplit && (
textOptions: T.TextOptions, <div>
showingStemsAndRoots: boolean, <button
highlightInRootsAndStems?: T.RootsOrStemsToHighlight, className="btn btn-sm btn-outline-secondary"
toggleShowingSar: () => void, onClick={() => setSplit(!split)}
hidePastParticiple?: boolean, >
hideTypeInfo?: boolean, {split ? "join" : "split"} head
}) { </button>
const inf = noPersInfs(info.root.imperfective).long; </div>
return ( )}
<div className="my-3"> </div>
{!hideTypeInfo && <VerbTypeInfo </div>
info={info} </div>
textOptions={textOptions} <div className={rowClass}>
/>} <div
<Hider className={colClass}
showing={showingStemsAndRoots} style={
label={`🌳 Roots and Stems for ${inf.p}`} highlighted?.includes("imperfective stem") ? highlight : {}
handleChange={toggleShowingSar} }
hLevel={4}
> >
<RootsAndStems <div style={title as any}>
textOptions={textOptions} <div>Imperfective Stem</div>
info={info} </div>
highlighted={highlightInRootsAndStems} <div style={indentR}>
hidePastParticiple={hidePastParticiple} <VerbInfoItemDisplay
item={pickPersInf(info.stem.imperfective, persInf)}
textOptions={textOptions}
tails={!noTails}
/> />
</Hider> </div>
</div>
<div
className={colClass}
style={highlighted?.includes("perfective stem") ? highlight : {}}
>
<div style={title as any}>
<div>Perfective Stem</div>
</div>
<div style={indentR}>
<VerbInfoItemDisplay
item={pickPersInf(perfectiveStem, persInf)}
textOptions={textOptions}
tails={!noTails}
/>
</div>
</div>
</div>
<div className={rowClass}>
<div
className={colClass}
style={
highlighted?.includes("imperfective root") ? highlight : {}
}
>
<div style={title as any}>
<div>Imperfective Root</div>
</div>
<div style={indentR}>
<VerbInfoItemDisplay
item={pickPersInf(info.root.imperfective, persInf)}
textOptions={textOptions}
/>
</div>
</div>
<div
className={colClass}
style={highlighted?.includes("perfective root") ? highlight : {}}
>
<div>
<div style={title as any}>
<div>Perfective Root</div>
</div>
<div style={indentR}>
<VerbInfoItemDisplay
item={pickPersInf(perfectiveRoot, persInf)}
textOptions={textOptions}
/>
</div>
</div>
</div>
</div>
{!hidePastParticiple && "participle" in info && (
<div
className="text-center"
style={highlighted?.includes("past participle") ? highlight : {}}
>
<div style={title as any}>Past Participle</div>
<VerbInfoItemDisplay
item={pickPersInf(info.participle.past, persInf)}
textOptions={textOptions}
/>
</div>
)}
</div> </div>
); </div>
</div>
);
} }
export default VerbInfo; function VerbInfo({
info,
textOptions,
showingStemsAndRoots,
toggleShowingSar,
highlightInRootsAndStems,
hidePastParticiple,
hideTypeInfo,
}: {
info: T.NonComboVerbInfo;
textOptions: T.TextOptions;
showingStemsAndRoots: boolean;
highlightInRootsAndStems?: T.RootsOrStemsToHighlight;
toggleShowingSar: () => void;
hidePastParticiple?: boolean;
hideTypeInfo?: boolean;
}) {
const inf = noPersInfs(info.root.imperfective).long;
return (
<div className="my-3">
{!hideTypeInfo && <VerbTypeInfo info={info} textOptions={textOptions} />}
<Hider
showing={showingStemsAndRoots}
label={`🌳 Roots and Stems for ${inf.p}`}
handleChange={toggleShowingSar}
hLevel={4}
>
<RootsAndStems
textOptions={textOptions}
info={info}
highlighted={highlightInRootsAndStems}
hidePastParticiple={hidePastParticiple}
/>
</Hider>
</div>
);
}
export default VerbInfo;

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({
const [showing, setShowing] = useStickyState<string[]>([], "VPTensesShowing"); VS,
const [showFormulas, setShowFormulas] = useStickyState<boolean>(false, "showFormulasWithCharts"); opts,
const adjustShowing = (v: string) => { }: {
if (showing.includes(v)) { VS: T.VerbSelection;
setShowing(os => os.filter(x => x !== v)); opts: T.TextOptions;
} else { }) {
setShowing(os => [v, ...os]); const [showing, setShowing] = useStickyState<string[]>([], "VPTensesShowing");
} const [showFormulas, setShowFormulas] = useStickyState<boolean>(
false,
"showFormulasWithCharts"
);
const adjustShowing = (v: string) => {
if (showing.includes(v)) {
setShowing((os) => os.filter((x) => x !== v));
} else {
setShowing((os) => [v, ...os]);
} }
const options = VS.tenseCategory === "basic" };
? verbTenseOptions const options =
: VS.tenseCategory === "perfect" VS.tenseCategory === "basic"
? perfectTenseOptions ? verbTenseOptions
: VS.tenseCategory === "modal" : VS.tenseCategory === "perfect"
? abilityTenseOptions ? perfectTenseOptions
: imperativeTenseOptions; : VS.tenseCategory === "modal"
const rawConjugations = conjugateVerb(VS.verb.entry, VS.verb.complement); ? abilityTenseOptions
const conjugations = ("stative" in rawConjugations) : imperativeTenseOptions;
? rawConjugations[VS.isCompound === "stative" ? "stative" : "dynamic"] // const rawConjugations = conjugateVerb(VS.verb.entry, VS.verb.complement);
: ("transitive" in rawConjugations) // const conjugations =
? rawConjugations[VS.transitivity === "grammatically transitive" ? "grammaticallyTransitive" : "transitive"] // "stative" in rawConjugations
: rawConjugations; // ? rawConjugations[VS.isCompound === "stative" ? "stative" : "dynamic"]
function getTense(baseTense: T.VerbTense | T.PerfectTense | T.ImperativeTense): T.VerbTense | T.PerfectTense | T.ImperativeTense | T.AbilityTense { // : "transitive" in rawConjugations
return VS.tenseCategory === "modal" ? `${baseTense}Modal` as T.AbilityTense : baseTense; // ? rawConjugations[
} // VS.transitivity === "grammatically transitive"
return <div> // ? "grammaticallyTransitive"
<div className="clickable mb-2 small text-center" onClick={() => setShowFormulas(x => !x)}> // : "transitive"
🧪 {!showFormulas ? "Show" : "Hide"} Formulas // ]
</div> // : rawConjugations;
{options.map((tense) => <div key={Math.random()}> function getTense(
baseTense: T.VerbTense | T.PerfectTense | T.ImperativeTense
): T.VerbTense | T.PerfectTense | T.ImperativeTense | T.AbilityTense {
return VS.tenseCategory === "modal"
? (`${baseTense}Modal` as T.AbilityTense)
: baseTense;
}
return (
<div>
<div
className="clickable mb-2 small text-center"
onClick={() => setShowFormulas((x) => !x)}
>
🧪 {!showFormulas ? "Show" : "Hide"} Formulas
</div>
{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 && (
<samp>{tense.formula}</samp> <div className="mb-1">
</div>} <samp>{tense.formula}</samp>
</div>
)}
{showing && (
<ChartDisplay <ChartDisplay
conjugations={conjugations} verb={VS.verb}
tense={getTense(tense.value)} negative={VS.negative}
voice={VS.voice} tense={t}
opts={opts} transitivity={VS.transitivity}
voice={VS.voice}
imperative={isImperativeTense(t)}
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,
/> });
</div>; return (
<div className="mb-4">
<NewVerbFormDisplay
chart={verbChart}
opts={opts}
transitivity={transitivity}
past={isPastTense(tense)}
/>
</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

@ -10,50 +10,51 @@ import * as T from "../../types";
import { fmapSingleOrLengthOpts } from "./fmaps"; import { fmapSingleOrLengthOpts } from "./fmaps";
export const blank: T.PsString = { export const blank: T.PsString = {
p: "_____", p: "_____",
f: "_____", f: "_____",
}; };
export const kidsBlank: T.PsString = { p: "___", f: "___" }; export const kidsBlank: T.PsString = { p: "___", f: "___" };
/** /**
* returns the main entry of a VerbEntry or just the entry of a DictionaryEntry * returns the main entry of a VerbEntry or just the entry of a DictionaryEntry
* *
* @param e FullEntry * @param e FullEntry
* @returns DictionaryEntry * @returns DictionaryEntry
*/ */
export function entryOfFull(e: T.FullEntry): T.DictionaryEntry { export function entryOfFull(e: T.FullEntry): T.DictionaryEntry {
return "entry" in e ? e.entry : e; return "entry" in e ? e.entry : e;
} }
// just for type safety // just for type safety
export function noPersInfs<S extends object>(s: T.OptionalPersonInflections<S>): S { export function noPersInfs<S extends object>(
if ("mascSing" in s) { s: T.OptionalPersonInflections<S>
// this path shouldn't be used, just for type safety ): S {
return s.mascSing; if ("mascSing" in s) {
} // this path shouldn't be used, just for type safety
return s; return s.mascSing;
}
return 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>(
// @ts-ignore s: T.OptionalPersonInflections<T>,
if ("mascSing" in s) { persInf: T.PersonInflectionsField
return s[persInf]; ): T {
} // @ts-ignore
return s; if ("mascSing" in s) {
return s[persInf];
}
return s;
} }
export function getFirstSecThird(p: T.Person): 1 | 2 | 3 { export function getFirstSecThird(p: T.Person): 1 | 2 | 3 {
if ([0, 1, 6, 7].includes(p)) return 1; if ([0, 1, 6, 7].includes(p)) return 1;
if ([2, 3, 8, 9].includes(p)) return 2; if ([2, 3, 8, 9].includes(p)) return 2;
return 3; return 3;
} }
// export function pickPersInf( // export function pickPersInf(
@ -82,140 +83,169 @@ 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(
if ("participle" in info) { info: T.NonComboVerbInfo | T.PassiveRootsAndStems | T.AbilityRootsAndStems
return ( ): boolean {
"mascSing" in info.root.perfective || if ("participle" in info) {
"mascSing" in info.stem.perfective ||
("present" in info.participle && "mascSing" in info.participle.present) ||
"mascSing" in info.participle.past
);
}
return ( return (
"mascSing" in info.root.perfective || "mascSing" in info.root.perfective ||
"mascSing" in info.stem.perfective "mascSing" in info.stem.perfective ||
("present" in info.participle && "mascSing" in info.participle.present) ||
"mascSing" in info.participle.past
); );
}
return (
"mascSing" in info.root.perfective || "mascSing" in info.stem.perfective
);
} }
// TODO: deprecated using new verb rendering thing // 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 {
short: chooseParticipleInflection(pPartInfs.short, person) as T.PsString, short: chooseParticipleInflection(pPartInfs.short, person) as T.PsString,
long: chooseParticipleInflection(pPartInfs.long, person) as T.PsString, long: chooseParticipleInflection(pPartInfs.long, person) as T.PsString,
}; };
} }
if ("masc" in pPartInfs) { if ("masc" in pPartInfs) {
const gender = personGender(person); const gender = personGender(person);
const infNum = personIsPlural(person) ? 1 : 0; const infNum = personIsPlural(person) ? 1 : 0;
return pPartInfs[gender][infNum][0]; return pPartInfs[gender][infNum][0];
} }
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(
const base = gender === "masc" ? 4 : 5; gender: T.Gender,
return base + (number === "singular" ? 0 : 6); number: T.NounNumber
): T.Person {
const base = gender === "masc" ? 4 : 5;
return base + (number === "singular" ? 0 : 6);
} }
export function personFromVerbBlockPos(pos: [number, number]): T.Person { 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 {
if ("mascSing" in form) { if ("mascSing" in form) {
return spaceInForm(form.mascSing); return spaceInForm(form.mascSing);
} }
if ("long" in form) { if ("long" in form) {
return spaceInForm(form.long); return spaceInForm(form.long);
} }
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>,
const [row, col] = getVerbBlockPosFromPerson(person); person: T.Person
return x[row][col]; ): T.SentenceForm {
}, form); return fmapSingleOrLengthOpts((x) => {
const [row, col] = getVerbBlockPosFromPerson(person);
return x[row][col];
}, form);
} }
export function getVerbBlockPosFromPerson(person: T.Person): [0 | 1 | 2 | 3 | 4 | 5, 0 | 1] { 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 col = plural ? 1 : 0; const plural = personIsPlural(person);
return [row, col]; const row = (plural ? person - 6 : person) as 0 | 1 | 2 | 3 | 4 | 5;
const col = plural ? 1 : 0;
return [row, col];
} }
export function getAuxTransitivity(trans: T.Transitivity): "transitive" | "intransitive" { export function getAuxTransitivity(
return trans === "intransitive" ? "intransitive" : "transitive"; trans: T.Transitivity
): "transitive" | "intransitive" {
return trans === "intransitive" ? "intransitive" : "transitive";
} }
export function personGender(person: T.Person): T.Gender { export function personGender(person: T.Person): T.Gender {
return person % 2 === 0 ? "masc" : "fem"; return person % 2 === 0 ? "masc" : "fem";
} }
export function personNumber(person: T.Person): T.NounNumber { export function personNumber(person: T.Person): T.NounNumber {
return personIsPlural(person) ? "plural" : "singular"; return personIsPlural(person) ? "plural" : "singular";
} }
export function personIsPlural(person: T.Person): boolean { 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,
? "1st" version?: "short" | "long"
: [2,3,8,9].includes(person) ): string {
? "2nd" const p =
: "3rd") + " pers."; ([0, 1, 6, 7].includes(person)
const number = personIsPlural(person) ? "plur" : "sing"; ? "1st"
const n = version === "short" : [2, 3, 8, 9].includes(person)
? (number === "plur" ? "pl" : "sg") : number; ? "2nd"
const gender = personGender(person); : "3rd") + " pers.";
const g = version === "short" const number = personIsPlural(person) ? "plur" : "sing";
? (gender === "masc" ? "m" : "f") const n = version === "short" ? (number === "plur" ? "pl" : "sg") : number;
: gender; const gender = personGender(person);
return `${p} ${n}. ${g}.`; const g = version === "short" ? (gender === "masc" ? "m" : "f") : gender;
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),
number: personNumber(p), number: personNumber(p),
}; };
} }
export function getEnglishParticipleInflection(person: T.Person, version?: "short" | "long"): string { export function getEnglishParticipleInflection(
const number = personIsPlural(person) ? "plural" : "singular"; person: T.Person,
const n = version === "short" version?: "short" | "long"
? (number === "plural" ? "plur." : "sing.") : number; ): string {
const gender = personGender(person); const number = personIsPlural(person) ? "plural" : "singular";
const g = gender; const n =
return `${g}. ${n}`; version === "short" ? (number === "plural" ? "plur." : "sing.") : number;
const gender = personGender(person);
const g = gender;
return `${g}. ${n}`;
} }
export function randomNumber(minInclusive: number, maxExclusive: number): number { 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);
@ -223,87 +253,92 @@ export const isSecondPerson = (p: T.Person) => [2, 3, 8, 9].includes(p);
export const isThirdPerson = (p: T.Person) => [4, 5, 10, 11].includes(p); export const isThirdPerson = (p: T.Person) => [4, 5, 10, 11].includes(p);
export function incrementPerson(p: T.Person): T.Person { export function incrementPerson(p: T.Person): T.Person {
return (p + 1) % 12; return (p + 1) % 12;
} }
export function isSentenceForm(f: any): boolean { export function isSentenceForm(f: any): boolean {
if ("long" in f) { if ("long" in f) {
return isSentenceForm(f.long); return isSentenceForm(f.long);
} }
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(
if (!entry.c) { entry: T.DictionaryEntry
return false; ): "nounAdj" | "verb" | false {
} if (!entry.c) {
if (entry.c.includes("adj.") || entry.c.includes("n. m.") || entry.c.includes("n. f.")) {
return "nounAdj";
}
if (entry.c.slice(0, 3) === "v. ") {
return "verb";
}
return false; return false;
}
if (
entry.c.includes("adj.") ||
entry.c.includes("n. m.") ||
entry.c.includes("n. f.")
) {
return "nounAdj";
}
if (entry.c.slice(0, 3) === "v. ") {
return "verb";
}
return false;
} }
/** /**
* takes the ec field from a dictionary entry and produces an array of an EnglishVerbConjugation * takes the ec field from a dictionary entry and produces an array of an EnglishVerbConjugation
* for use with the conjugations display for showing English translation sentences of various verb * for use with the conjugations display for showing English translation sentences of various verb
* forms and conjugations * forms and conjugations
* *
* @param ec * @param ec
* @returns * @returns
*/ */
export function parseEc(ec: string): T.EnglishVerbConjugationEc { export function parseEc(ec: string): T.EnglishVerbConjugationEc {
function isVowel(s: string): boolean { function isVowel(s: string): boolean {
return ["a", "e", "i", "o", "u"].includes(s); return ["a", "e", "i", "o", "u"].includes(s);
}
function makeRegularConjugations(s: string): T.EnglishVerbConjugationEc {
if (s === "get") {
return ["get", "gets", "getting", "got", "gotten"];
} }
function makeRegularConjugations(s: string): T.EnglishVerbConjugationEc { if (s === "become") {
if (s === "get") { return ["become", "becomes", "becoming", "became", "become"];
return ["get","gets","getting","got","gotten"];
}
if (s === "become") {
return ["become","becomes","becoming","became","become"];
}
if (s === "make") {
return ["make","makes","making","made","made"];
}
if (s === "have") {
return ["have","has","having","had","had"];
}
if (s === "be") {
return ["am","is","being","was","been"];
}
if ((s.slice(-1) === "y") && !isVowel(s.slice(-2)[0])) {
const b = s.slice(0, -1);
return [`${s}`, `${b}ies`, `${s}ing`, `${b}ied`, `${b}ied`];
}
if (s.slice(-2) === "ss") {
return [`${s}`, `${s}es`, `${s}ing`, `${s}ed`, `${s}ed`];
}
if (s.slice(-2) === "ie" && !isVowel(s.slice(-3)[0])) {
const b = s.slice(0, -2);
return [`${s}`, `${s}s`, `${b}ying`, `${s}d`, `${s}d`];
}
const b = s === ""
? "VERB"
: (s.slice(-1) === "e")
? s.slice(0, -1)
: s;
return [`${s}`, `${s}s`, `${b}ing`, `${b}ed`, `${b}ed`];
} }
const items = ec.split(",").map(x => x.trim()); if (s === "make") {
return (items.length === 4) return ["make", "makes", "making", "made", "made"];
? [items[0], items[1], items[2], items[3], items[3]] }
: (items.length === 5) if (s === "have") {
? [items[0], items[1], items[2], items[3], items[4]] return ["have", "has", "having", "had", "had"];
: makeRegularConjugations(items[0]); }
if (s === "be") {
return ["am", "is", "being", "was", "been"];
}
if (s.slice(-1) === "y" && !isVowel(s.slice(-2)[0])) {
const b = s.slice(0, -1);
return [`${s}`, `${b}ies`, `${s}ing`, `${b}ied`, `${b}ied`];
}
if (s.slice(-2) === "ss") {
return [`${s}`, `${s}es`, `${s}ing`, `${s}ed`, `${s}ed`];
}
if (s.slice(-2) === "ie" && !isVowel(s.slice(-3)[0])) {
const b = s.slice(0, -2);
return [`${s}`, `${s}s`, `${b}ying`, `${s}d`, `${s}d`];
}
const b = s === "" ? "VERB" : s.slice(-1) === "e" ? s.slice(0, -1) : s;
return [`${s}`, `${s}s`, `${b}ing`, `${b}ed`, `${b}ed`];
}
const items = ec.split(",").map((x) => x.trim());
return items.length === 4
? [items[0], items[1], items[2], items[3], items[3]]
: items.length === 5
? [items[0], items[1], items[2], items[3], items[4]]
: makeRegularConjugations(items[0]);
} }
export function chooseLength<N>(x: T.SingleOrLengthOpts<N>, length: "long" | "short"): N { export function chooseLength<N>(
// @ts-ignore x: T.SingleOrLengthOpts<N>,
if ("long" in x) { length: "long" | "short"
return x[length]; ): N {
} // @ts-ignore
return x; if ("long" in x) {
} return x[length];
}
return x;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,310 +1,367 @@
import * as T from "../../../types"; import * as T from "../../../types";
import { import {
getVerbBlockPosFromPerson, getVerbBlockPosFromPerson,
isSecondPerson, isSecondPerson,
personGender, personNumber,
personNumber, personToGenNum,
personToGenNum,
} from "../misc-helpers"; } from "../misc-helpers";
import { fmapSingleOrLengthOpts } from "../fmaps";
import { concatPsString, getLength } from "../p-text-helpers";
import { import {
fmapSingleOrLengthOpts, presentEndings,
} from "../fmaps"; pastEndings,
import { equativeEndings,
concatPsString, imperativeEndings,
getLength,
} from "../p-text-helpers";
import {
presentEndings,
pastEndings,
equativeEndings,
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, presentVerb: {
}, aspect: "imperfective",
"subjunctiveVerb": { tenseC: "present",
aspect: "imperfective", hasBa: false,
tenseC: "present", },
hasBa: false, subjunctiveVerb: {
}, aspect: "perfective",
"perfectiveFuture": { tenseC: "present",
aspect: "perfective", hasBa: false,
tenseC: "present", },
hasBa: true, perfectiveFuture: {
}, aspect: "perfective",
"imperfectiveFuture": { tenseC: "present",
aspect: "imperfective", hasBa: true,
tenseC: "present", },
hasBa: true, imperfectiveFuture: {
}, aspect: "imperfective",
"perfectivePast": { tenseC: "present",
aspect: "perfective", hasBa: true,
tenseC: "past", },
hasBa: false, perfectivePast: {
}, aspect: "perfective",
"imperfectivePast": { tenseC: "past",
aspect: "imperfective", hasBa: false,
tenseC: "past", },
hasBa: false, imperfectivePast: {
}, aspect: "imperfective",
"habitualImperfectivePast": { tenseC: "past",
aspect: "imperfective", hasBa: false,
tenseC: "past", },
hasBa: true, habitualImperfectivePast: {
}, aspect: "imperfective",
"habitualPerfectivePast": { tenseC: "past",
aspect: "perfective", hasBa: true,
tenseC: "past", },
hasBa: true, habitualPerfectivePast: {
}, aspect: "perfective",
"perfectiveImperative": { tenseC: "past",
aspect: "perfective", hasBa: true,
tenseC: "imperative", },
hasBa: false, perfectiveImperative: {
}, aspect: "perfective",
"imperfectiveImperative": { tenseC: "imperative",
aspect: "imperfective", hasBa: false,
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 }: { if (isPerfectTense(tense)) {
verb: T.VerbEntry, return renderPerfectVerb({
negative: boolean, verb,
tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense, // TODO: make T.Tense tense,
person: T.Person, voice,
complementGenNum: { gender: T.Gender, number: T.NounNumber }, person: object ?? subject,
voice: T.Voice,
}): {
hasBa: boolean,
vbs: T.VerbRenderedOutput,
} {
if (isPerfectTense(tense)) {
return renderPerfectVerb({ verb, tense, voice, person });
}
const { aspect, tenseC, hasBa } = formulas[removeAbility(tense)];
const isPast = tenseC === "past";
const type = isAbilityTense(tense) ? "ability" : "basic";
// #1 get the appropriate root / stem
const [vHead, rest] = getRootStem({
verb,
rs: isPast ? "root" : "stem",
aspect,
voice,
type,
genderNumber: complementGenNum,
}); });
// #2 add the verb ending to it }
const ending = getEnding(person, tenseC, aspect); const { aspect, tenseC, hasBa } = formulas[removeAbility(tense)];
return { const isPast = tenseC === "past";
hasBa, const type = isAbilityTense(tense) ? "ability" : "basic";
vbs: [ const transitive = object !== undefined;
vHead, const king = transitive && isPast ? object : subject;
addEnding({
rs: rest, // #1 get the appropriate root / stem
ending, const [vHead, rest] = getRootStem({
verb, verb,
person, rs: isPast ? "root" : "stem",
pastThird: isPast && person === T.Person.ThirdSingMale, aspect,
aspect, voice,
basicForm: type === "basic" && voice === "active", type,
}), genderNumber: personToGenNum(transitive ? object : subject),
], });
};
// #2 add the verb ending to it
const ending = getEnding(king, tenseC, aspect);
return {
hasBa,
objComp: undefined,
vbs: [
vHead,
addEnding({
rs: rest,
ending,
verb,
person: king,
pastThird: isPast && king === T.Person.ThirdSingMale,
aspect,
basicForm: type === "basic" && voice === "active",
}),
],
};
} }
function renderPerfectVerb({ tense, verb, voice, person }: { 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;
const hasBa = perfectTenseHasBa(tense); tense: T.PerfectTense;
// #1 get the past participle verb: T.VerbEntry;
const pp = getPastParticiple(verb, voice, personToGenNum(person)); voice: T.Voice;
// #2 get the right equative }): {
const equative = equativeEndings[perfectTenseToEquative(tense)]; hasBa: boolean;
const [row, col] = getVerbBlockPosFromPerson(person); vbs: [[], [T.VB, T.VBE]];
const equativeBlock: T.VBE = { objComp: T.Rendered<T.NPSelection> | undefined;
type: "VB", } {
person, const hasBa = perfectTenseHasBa(tense);
ps: fmapSingleOrLengthOpts(x => x[row][col], equative), // #1 get the past participle
}; const pp = getPastParticiple(verb, voice, personToGenNum(person));
return { // #2 get the right equative
hasBa, const equative = equativeEndings[perfectTenseToEquative(tense)];
vbs: [[], [pp, equativeBlock]], const [row, col] = getVerbBlockPosFromPerson(person);
}; const equativeBlock: T.VBE = {
type: "VB",
person,
ps: fmapSingleOrLengthOpts((x) => x[row][col], equative),
};
return {
hasBa,
objComp: undefined,
vbs: [[], [pp, equativeBlock]],
};
} }
function addEnding({ verb, rs, ending, person, pastThird, aspect, basicForm }: { 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(
if (vba.type === "welded") { vba: T.VBA,
return { ending: T.SingleOrLengthOpts<T.PsString[]>
...vba, ): T.VBE {
right: addToVBBasicEnd(vba.right, ending), if (vba.type === "welded") {
person, return {
}; ...vba,
} right: addToVBBasicEnd(vba.right, ending),
return { person,
...addToVBBasicEnd(vba, ending), };
person,
}
} }
function addToVBBasicEnd(vb: T.VBBasic, end: T.SingleOrLengthOpts<T.PsString[]>): T.VBBasic { return {
if ("long" in vb.ps) { ...addToVBBasicEnd(vba, ending),
if (vb.ps.short[0].f === "ghl" && pastThird && basicForm) { person,
return { };
...vb, }
ps: [{ p: "غی", f: "ghey" }], function addToVBBasicEnd(
}; vb: T.VBBasic,
} end: T.SingleOrLengthOpts<T.PsString[]>
const endLong = getLength(end, "long"); ): T.VBBasic {
const endShort = getLength(end, "short"); if ("long" in vb.ps) {
// TODO: this is hacky if (vb.ps.short[0].f === "ghl" && pastThird && basicForm) {
return {
...vb,
ps: {
long: verbEndingConcat(vb.ps.long, endLong),
short: pastThird && basicForm
? ensure3rdPast(vb.ps.short, endShort, verb, aspect)
: verbEndingConcat(vb.ps.short, endShort),
...vb.ps.mini ? {
mini: verbEndingConcat(vb.ps.mini, endShort),
} : {},
},
};
}
/* istanbul ignore next */
if ("long" in end) {
throw new Error("should not be using verb stems with long and short endings");
}
return { return {
...vb, ...vb,
ps: verbEndingConcat(vb.ps, end), ps: [{ p: "غی", f: "ghey" }],
}; };
}
const endLong = getLength(end, "long");
const endShort = getLength(end, "short");
// TODO: this is hacky
return {
...vb,
ps: {
long: verbEndingConcat(vb.ps.long, endLong),
short:
pastThird && basicForm
? ensure3rdPast(vb.ps.short, endShort, verb, aspect)
: verbEndingConcat(vb.ps.short, endShort),
...(vb.ps.mini
? {
mini: verbEndingConcat(vb.ps.mini, endShort),
}
: {}),
},
};
} }
/* istanbul ignore next */
if ("long" in end) {
throw new Error(
"should not be using verb stems with long and short endings"
);
}
return {
...vb,
ps: verbEndingConcat(vb.ps, end),
};
}
} }
function getEnding(person: T.Person, tenseC: "present" | "past" | "imperative", aspect: T.Aspect) { function getEnding(
if (tenseC === "imperative") { person: T.Person,
if (!isSecondPerson(person)) { tenseC: "present" | "past" | "imperative",
throw new Error("imperative forms must be second person"); aspect: T.Aspect
} ) {
const number = personNumber(person); if (tenseC === "imperative") {
const ends = imperativeEndings[0][number === "singular" ? 0 : 1]; if (!isSecondPerson(person)) {
return aspect === "imperfective" throw new Error("imperative forms must be second person");
? ends.map(e => accentPsSyllable(e))
: ends;
} }
const [row, col] = getVerbBlockPosFromPerson(person); const number = personNumber(person);
return tenseC === "past" ? { const ends = imperativeEndings[0][number === "singular" ? 0 : 1];
return aspect === "imperfective"
? ends.map((e) => accentPsSyllable(e))
: ends;
}
const [row, col] = getVerbBlockPosFromPerson(person);
return tenseC === "past"
? {
long: pastEndings.long[row][col], 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(
if (isKedul(verb)) { rs: T.PsString[],
return aspect === "perfective" ending: T.PsString[],
? [{ p: "شو", f: "sho" }] verb: T.VerbEntry,
: [{ p: "کېده", f: "kedú" }]; aspect: T.Aspect
} ): T.PsString[] {
if (isKawulVerb(verb) && rs[0].p === "کړ") { if (isKedul(verb)) {
return [ return aspect === "perfective"
{ p: "کړ", f: "kuR" }, ? [{ p: "شو", f: "sho" }]
{ p: "کړه", f: "kRu" }, : [{ p: "کېده", f: "kedú" }];
{ p: "کړو", f: "kRo" }, }
]; if (isKawulVerb(verb) && rs[0].p === "کړ") {
} return [
if (isTlulVerb(verb)) { { p: "کړ", f: "kuR" },
// should be imperfective at this point { p: "کړه", f: "kRu" },
// the perfective غی should already be covered in the function this is coming from { p: "کړو", f: "kRo" },
return [{ ];
p: rs[0].p.slice(0, -1) + "ه", }
f: rs[0].f.slice(0, -2) + "ú", if (isTlulVerb(verb)) {
}]; // should be imperfective at this point
} // the perfective غی should already be covered in the function this is coming from
if (verb.entry.tppp && verb.entry.tppf) { return [
const tip = removeAccents(verb.entry.separationAtP !== undefined {
? makePsString(verb.entry.tppp.slice(verb.entry.separationAtP), verb.entry.tppf.slice(verb.entry.separationAtF)) p: rs[0].p.slice(0, -1) + "ه",
: makePsString(verb.entry.tppp, verb.entry.tppf)); f: rs[0].f.slice(0, -2) + "ú",
const aTip = aspect === "imperfective" },
? accentOnNFromEnd(tip, 0) {
: tip; p: rs[0].p + "و",
return [aTip]; f: rs[0].f.slice(0, -1) + "ó",
// if it ends in a consonant, the special form will also have another },
// variation ending with a ه - u ];
// const endsInAConsonant = (pashtoConsonants.includes(tip.p.slice(-1)) || tip.f.slice(-1) === "w"); }
// return [ if (verb.entry.tppp && verb.entry.tppf) {
// aTip, const tip = removeAccents(
// ...endsInAConsonant ? [ verb.entry.separationAtP !== undefined
// ...verbEndingConcat([aTip], [{ p: "ه", f: "u" }, { p: "و", f: "o" }]), ? makePsString(
// ] : [], verb.entry.tppp.slice(verb.entry.separationAtP),
// ]; verb.entry.tppf.slice(verb.entry.separationAtF)
} )
const endsInAwul = ( : makePsString(verb.entry.tppp, verb.entry.tppf)
(["awul", "awúl"].includes(removeFVarients(verb.entry.f).slice(-4)))
&&
(verb.entry.p.slice(-2) === "ول")
); );
// TODO: check about verbs like tawul (if they exist) const aTip = aspect === "imperfective" ? accentOnNFromEnd(tip, 0) : tip;
if (endsInAwul) { return [aTip];
const base = { p: rs[0].p.slice(0, -1), f: rs[0].f.slice(0, -2) }; // if it ends in a consonant, the special form will also have another
const aawuEnd = concatPsString(base, { p: "اوه", f: base.f.charAt(base.f.length-1) === "a" ? "awu" : "aawu" }); // variation ending with a ه - u
return [aspect === "imperfective" // const endsInAConsonant = (pashtoConsonants.includes(tip.p.slice(-1)) || tip.f.slice(-1) === "w");
? accentOnNFromEnd(aawuEnd, 0) // return [
: aawuEnd]; // aTip,
} // ...endsInAConsonant ? [
const endsInDental = ["د", "ت"].includes(rs[0].p.slice(-1)); // ...verbEndingConcat([aTip], [{ p: "ه", f: "u" }, { p: "و", f: "o" }]),
// short endings like ورسېد // ] : [],
const ends = endsInDental ? [{ p: "", f: "" }, ...ending] : ending; // ];
return verbEndingConcat(rs, ends); }
const endsInAwul =
["awul", "awúl"].includes(removeFVarients(verb.entry.f).slice(-4)) &&
verb.entry.p.slice(-2) === "ول";
// TODO: check about verbs like tawul (if they exist)
if (endsInAwul) {
const base = { p: rs[0].p.slice(0, -1), f: rs[0].f.slice(0, -2) };
const aawuEnd = concatPsString(base, {
p: "اوه",
f: base.f.charAt(base.f.length - 1) === "a" ? "awu" : "aawu",
});
return [aspect === "imperfective" ? accentOnNFromEnd(aawuEnd, 0) : aawuEnd];
}
const endsInDental = ["د", "ت"].includes(rs[0].p.slice(-1));
// short endings like ورسېد
const ends = endsInDental ? [{ p: "", f: "" }, ...ending] : ending;
return verbEndingConcat(rs, ends);
} }
function removeAbility(tense: T.VerbTense | T.AbilityTense | T.ImperativeTense): T.VerbTense | T.ImperativeTense { function removeAbility(
return tense.replace("Modal", "") as T.VerbTense | T.ImperativeTense; tense: T.VerbTense | T.AbilityTense | T.ImperativeTense
} ): T.VerbTense | T.ImperativeTense {
return tense.replace("Modal", "") as T.VerbTense | T.ImperativeTense;
}

File diff suppressed because it is too large Load Diff

View File

@ -6,360 +6,471 @@
* *
*/ */
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 = {
type: "VB", type: "VB",
ps: { ps: {
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,
genderNumber: { voice,
gender: T.Gender, }: {
number: T.NounNumber, verb: T.VerbEntry;
}, rs: "root" | "stem";
aspect: T.Aspect;
voice: T.Voice;
type: "basic" | "ability";
genderNumber: {
gender: T.Gender;
number: T.NounNumber;
};
}): T.RootsStemsOutput { }): T.RootsStemsOutput {
const v = removeFVarientsFromVerb(verb); const v = removeFVarientsFromVerb(verb);
if (type === "ability") { if (type === "ability") {
return getAbilityRs(v, aspect, rs, voice, genderNumber); return getAbilityRs(v, aspect, rs, voice, genderNumber);
} }
if (voice === "passive") { if (voice === "passive") {
return getPassiveRs(v, aspect, rs, genderNumber); return getPassiveRs(v, aspect, rs, genderNumber);
} }
return rs === "stem" return rs === "stem"
? getStem(v, genderNumber, aspect) ? getStem(v, genderNumber, aspect)
: getRoot(v, genderNumber, aspect); : getRoot(v, genderNumber, aspect);
} }
function getAbilityRs( function getAbilityRs(
verb: T.VerbEntryNoFVars, verb: T.VerbEntryNoFVars,
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) ||
? getPassiveRs(verb, "imperfective", "root", genderNum) (isStatComp(verb) && vTransitivity(verb) === "intransitive");
: getRoot(verb, genderNum, losesAspect ? "imperfective" : aspect); const [vhead, [basicroot]] =
return [ voice === "passive"
vhead, ? getPassiveRs(verb, "imperfective", "root", genderNum)
[ : getRoot(verb, genderNum, losesAspect ? "imperfective" : aspect);
addAbilityEnding(basicroot), return [vhead, [addAbilityEnding(basicroot), rs === "root" ? shwulVB : shVB]];
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(
const v = removeFVarientsFromVerb(verb); verb: T.VerbEntry,
if (voice === "passive") { voice: T.Voice,
return getPassivePp(v, { gender, number }); { gender, number }: { gender: T.Gender; number: T.NounNumber }
} ): T.VBGenNum | T.WeldedGN {
if (isStatComp(v) && v.complement) { const v = removeFVarientsFromVerb(verb);
return weld( if (voice === "passive") {
makeComplement(v.complement, { gender, number }), return getPassivePp(v, { gender, number });
getPastParticiple( }
statVerb[vTransitivity(verb)], if (isStatComp(v) && v.complement) {
voice, return weld(
{ gender, number }, makeComplement(v.complement, { gender, number }),
) as T.VBGenNum, getPastParticiple(statVerb[vTransitivity(verb)], voice, {
);
}
if (verb.entry.pprtp && verb.entry.pprtf) {
const base = makePsString(verb.entry.pprtp, verb.entry.pprtf);
return {
type: "VB",
ps: inflectPattern3(base, { gender, number }),
gender,
number,
};
}
const basicRoot = getRoot(removeFVarientsFromVerb(verb), { gender, number }, "imperfective")[1][0];
const longRoot = getLongVB(basicRoot);
const rootWLengths = possiblePPartLengths(longRoot);
/* istanbul ignore next */
if ("right" in rootWLengths) {
throw new Error("should not have welded here");
}
return {
...rootWLengths,
ps: addTail(rootWLengths.ps),
gender, gender,
number, number,
}) as T.VBGenNum
);
}
if (verb.entry.pprtp && verb.entry.pprtf) {
const base = makePsString(verb.entry.pprtp, verb.entry.pprtf);
return {
type: "VB",
ps: inflectPattern3(base, { gender, number }),
gender,
number,
}; };
}
function addTail(ps: T.SingleOrLengthOpts<T.PsString[]>): T.SingleOrLengthOpts<T.PsString[]> { const basicRoot = getRoot(
return fmapSingleOrLengthOpts((x) => { removeFVarientsFromVerb(verb),
const withTail = concatPsString(x[0], { p: "ی", f: "ey"}); { gender, number },
return inflectPattern3(withTail, { gender, number }); "imperfective"
}, ps); )[1][0];
} const longRoot = getLongVB(basicRoot);
const rootWLengths = possiblePPartLengths(longRoot);
/* istanbul ignore next */
if ("right" in rootWLengths) {
throw new Error("should not have welded here");
}
return {
...rootWLengths,
ps: addTail(rootWLengths.ps),
gender,
number,
};
function addTail(
ps: T.SingleOrLengthOpts<T.PsString[]>
): T.SingleOrLengthOpts<T.PsString[]> {
return fmapSingleOrLengthOpts((x) => {
const withTail = concatPsString(x[0], { p: "ی", f: "ey" });
return inflectPattern3(withTail, { gender, number });
}, ps);
}
} }
function getPassivePp(verb: T.VerbEntryNoFVars, genderNumber: T.GenderNumber): T.WeldedGN { function getPassivePp(
if (isStatComp(verb) && verb.complement) { verb: T.VerbEntryNoFVars,
return weld( genderNumber: T.GenderNumber
makeComplement(verb.complement, genderNumber), ): T.WeldedGN {
getPassivePp(statVerb.transitive, genderNumber), if (isStatComp(verb) && verb.complement) {
return weld(
makeComplement(verb.complement, genderNumber),
getPassivePp(statVerb.transitive, genderNumber)
);
}
const basicRoot = getRoot(
verb,
genderNumber,
isKawulVerb(verb) ? "perfective" : "imperfective"
)[1][0];
const longRoot = getLongVB(basicRoot);
const kedulVb: T.VBGenNum = getPastParticiple(
statVerb.intransitive,
"active",
genderNumber
) as T.VBGenNum;
return weld(longRoot, kedulVb);
}
function getPassiveRs(
verb: T.VerbEntryNoFVars,
aspect: T.Aspect,
rs: "root" | "stem",
genderNumber: T.GenderNumber
): [[] | [T.VHead], [T.VBA]] {
const [vHead, [basicRoot]] = getRoot(verb, genderNumber, aspect);
const longRoot = getLongVB(basicRoot);
const kedulVba = getRootStem({
verb: statVerb.intransitive,
aspect,
rs,
type: "basic",
voice: "active",
genderNumber: { gender: "masc", number: "singular" },
})[1][0] as T.VBBasic;
return [vHead, [weld(longRoot, kedulVba)]];
}
function getRoot(
verb: T.VerbEntryNoFVars,
genderNum: T.GenderNumber,
aspect: T.Aspect
): [[T.VHead] | [], [T.VBA]] {
if (
verb.complement &&
isStatComp(verb) &&
(aspect === "perfective" || statCompImperfectiveSpace(verb))
) {
const auxStem = getRoot(
statVerb[vTransitivity(verb)],
genderNum,
aspect
)[1][0] as T.VBBasic;
const complement = makeComplement(verb.complement, genderNum);
return aspect === "perfective"
? [[complement], [auxStem]]
: [[], [weld(complement, auxStem)]];
}
const base =
aspect === "imperfective"
? accentOnNFromEnd(makePsString(verb.entry.p, verb.entry.f), 0)
: removeAccents(
verb.entry.prp && verb.entry.prf
? makePsString(verb.entry.prp, verb.entry.prf)
: makePsString(verb.entry.p, verb.entry.f)
); );
} const [perfectiveHead, rest] =
const basicRoot = getRoot(verb, genderNumber, isKawulVerb(verb) ? "perfective" : "imperfective")[1][0]; aspect === "perfective" ? getPerfectiveHead(base, verb) : [undefined, base];
const longRoot = getLongVB(basicRoot); if (verb.entry.f === "tlul" && aspect === "perfective") {
const kedulVb: T.VBGenNum = getPastParticiple(statVerb.intransitive, "active", genderNumber) as T.VBGenNum;
return weld(longRoot, kedulVb);
}
function getPassiveRs(verb: T.VerbEntryNoFVars, aspect: T.Aspect, rs: "root" | "stem", genderNumber: T.GenderNumber): [[] | [T.VHead], [T.VBA]] {
const [vHead, [basicRoot]] = getRoot(verb, genderNumber, aspect);
const longRoot = getLongVB(basicRoot);
const kedulVba = getRootStem({ verb: statVerb.intransitive, aspect, rs, type: "basic", voice: "active", genderNumber: { gender: "masc", number: "singular" }})[1][0] as T.VBBasic;
return [ return [
vHead, [{ type: "PH", ps: { p: "لا", f: "láa" } }],
[weld(longRoot, kedulVba)], [
{
type: "VB",
ps: {
long: [{ p: "ړل", f: "Rul" }],
short: [{ p: "ړ", f: "R" }],
},
},
],
]; ];
} }
return [
function getRoot(verb: T.VerbEntryNoFVars, genderNum: T.GenderNumber, aspect: T.Aspect): [[T.VHead] | [], [T.VBA]] { perfectiveHead ? [perfectiveHead] : [],
if (verb.complement && isStatComp(verb) && (aspect === "perfective" || statCompImperfectiveSpace(verb))) { [
const auxStem = getRoot( {
statVerb[vTransitivity(verb)], type: "VB",
genderNum, ps:
aspect, aspect === "imperfective"
)[1][0] as T.VBBasic; ? {
const complement = makeComplement(verb.complement, genderNum); long: [rest],
return aspect === "perfective" short: [addTrailingAccent(removeL(rest))],
? [[complement], [auxStem]] }
: [[], [weld(complement, auxStem)]]; : {
} long: [rest],
const base = aspect === "imperfective" short: [removeL(rest)],
? accentOnNFromEnd(makePsString(verb.entry.p, verb.entry.f), 0) ...(aspect === "perfective" && isKawulVerb(verb)
: removeAccents( ? {
(verb.entry.prp && verb.entry.prf) mini: [{ p: "ک", f: "k" }],
? makePsString(verb.entry.prp, verb.entry.prf)
: makePsString(verb.entry.p, verb.entry.f)
);
const [perfectiveHead, rest] = aspect === "perfective"
? getPerfectiveHead(base, verb)
: [undefined, base];
return [
perfectiveHead ? [perfectiveHead] : [],
[
{
type: "VB",
ps: aspect === "imperfective"
? {
long: [rest],
short: [addTrailingAccent(removeL(rest))],
} }
: { : {}),
long: [rest], },
short: [removeL(rest)], },
...(aspect === "perfective" && isKawulVerb(verb)) ? { ],
mini: [{ p: "ک", f: "k" }], ];
} : {},
},
},
],
];
} }
function getStem(verb: T.VerbEntryNoFVars, genderNum: T.GenderNumber, aspect: T.Aspect): [[T.VHead] | [], [T.VB]] { function getStem(
const statComp = isStatComp(verb); verb: T.VerbEntryNoFVars,
if (verb.complement && statComp && (aspect === "perfective" || statCompImperfectiveSpace(verb))) { genderNum: T.GenderNumber,
const auxStem = getStem( aspect: T.Aspect
statVerb[vTransitivity(verb)], ): [[T.VHead] | [], [T.VB]] {
genderNum, const statComp = isStatComp(verb);
aspect, if (
)[1][0] as T.VBBasic; verb.complement &&
const complement = makeComplement(verb.complement, genderNum); statComp &&
return aspect === "perfective" (aspect === "perfective" || statCompImperfectiveSpace(verb))
? [[complement], [auxStem]] ) {
: [[], [weld(complement, auxStem)]]; const auxStem = getStem(
statVerb[vTransitivity(verb)],
genderNum,
aspect
)[1][0] as T.VBBasic;
const complement = makeComplement(verb.complement, genderNum);
return aspect === "perfective"
? [[complement], [auxStem]]
: [[], [weld(complement, auxStem)]];
}
if (aspect === "perfective") {
if (verb.entry.f === "tlul") {
return tlulPerfectiveStem(genderNum);
} }
if (aspect === "perfective") { if (
if (verb.entry.f === "tlul") { !isKedul(verb) &&
return tlulPerfectiveStem(genderNum); vTransitivity(verb) === "intransitive" &&
} verb.entry.p.endsWith("ېدل")
if (!isKedul(verb) && vTransitivity(verb) === "intransitive" && verb.entry.p.endsWith("ېدل")) { ) {
return splitEdulIntans(edulIntransBase(verb)) return splitEdulIntans(edulIntransBase(verb));
}
const base: T.PsString = (verb.entry.ssp && verb.entry.ssf)
// with irregular perfective stem
? makePsString(verb.entry.ssp, verb.entry.ssf)
: (verb.entry.psp && verb.entry.psf)
// with perfective stem based on irregular perfective root
? makePsString(verb.entry.psp, verb.entry.psf)
// with regular infinitive based perfective stem
: removeL(makePsString(verb.entry.p, verb.entry.f));
const [perfectiveHead, rest] = getPerfectiveHead(base, verb);
return [
perfectiveHead ? [perfectiveHead] : [],
[
{
type: "VB",
ps: isKawulVerb(verb) ? kawulSpecialPerfective : [rest],
},
],
];
} }
const rawBase = removeL(makePsString(verb.entry.p, verb.entry.f)); const base: T.PsString =
const base = verb.entry.psp && verb.entry.psf verb.entry.ssp && verb.entry.ssf
? [makePsString(verb.entry.psp, verb.entry.psf)] ? // with irregular perfective stem
: (vTransitivity(verb) === "intransitive" && rawBase.p.endsWith("ېد")) makePsString(verb.entry.ssp, verb.entry.ssf)
? edulIntransBase(verb) : verb.entry.psp && verb.entry.psf
: isKawulVerb(verb) || statComp || (countSyllables(rawBase) > 1 && rawBase.f.endsWith("aw")) ? // with perfective stem based on irregular perfective root
? [addTrailingAccent(rawBase)] makePsString(verb.entry.psp, verb.entry.psf)
: [rawBase]; : // with regular infinitive based perfective stem
removeL(makePsString(verb.entry.p, verb.entry.f));
const [perfectiveHead, rest] = getPerfectiveHead(base, verb);
return [ return [
[], perfectiveHead ? [perfectiveHead] : [],
[ [
{ {
type: "VB", type: "VB",
ps: base, ps: isKawulVerb(verb) ? kawulSpecialPerfective : [rest],
}, },
], ],
]; ];
function splitEdulIntans(ps: T.SingleOrLengthOpts<T.PsString[]>): [[T.PH] | [], [T.VB]] { }
const [ph, long] = ("long" in ps) const rawBase = removeL(makePsString(verb.entry.p, verb.entry.f));
? getPerfectiveHead(ps.long[0], verb) const base =
: getPerfectiveHead(ps[0], verb) verb.entry.psp && verb.entry.psf
const short = ("long" in ps) ? [makePsString(verb.entry.psp, verb.entry.psf)]
? getPerfectiveHead(ps.short[0], verb) : vTransitivity(verb) === "intransitive" && rawBase.p.endsWith("ېد")
: undefined; ? edulIntransBase(verb)
if (short) { : isKawulVerb(verb) ||
return [ statComp ||
ph ? [ph] : [], (countSyllables(rawBase) > 1 && rawBase.f.endsWith("aw"))
[ ? [addTrailingAccent(rawBase)]
{ : [rawBase];
type: "VB", return [
ps: { [],
long: [long], [
short: [short[1]], {
}, type: "VB",
}, ps: base,
], },
]; ],
} ];
return [ function splitEdulIntans(
ph ? [ph] : [], ps: T.SingleOrLengthOpts<T.PsString[]>
[ ): [[T.PH] | [], [T.VB]] {
{ type: "VB", ps: [long] }, const [ph, long] =
], "long" in ps
]; ? getPerfectiveHead(ps.long[0], verb)
: getPerfectiveHead(ps[0], verb);
const short =
"long" in ps ? getPerfectiveHead(ps.short[0], verb) : undefined;
if (short) {
return [
ph ? [ph] : [],
[
{
type: "VB",
ps: {
long: [long],
short: [short[1]],
},
},
],
];
} }
return [ph ? [ph] : [], [{ type: "VB", ps: [long] }]];
}
} }
// TODO: This is a nasty and messy way to do it with the length options included // 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(
// if ((verb.entry.ssp && verb.entry.ssf) || verb.entry.separationAtP) { base: T.PsString,
// // handle split v: T.VerbEntryNoFVars
// } ): [T.PH, T.PsString] | [undefined, T.PsString] {
if (v.entry.separationAtP && v.entry.separationAtF) { // if ((verb.entry.ssp && verb.entry.ssf) || verb.entry.separationAtP) {
const ph: T.PH = { // // handle split
type: "PH", // }
ps: accentOnNFromEnd({ if (v.entry.separationAtP && v.entry.separationAtF) {
p: base.p.slice(0, v.entry.separationAtP), const ph: T.PH = {
f: base.f.slice(0, v.entry.separationAtF), type: "PH",
}, 0), ps: accentOnNFromEnd(
}; {
const rest = { p: base.p.slice(0, v.entry.separationAtP),
p: base.p.slice(v.entry.separationAtP), f: base.f.slice(0, v.entry.separationAtF),
f: base.f.slice(v.entry.separationAtF), },
}; 0
return [ph, rest]; ),
} };
const [ph, rest]: [T.PH | undefined, T.PsString] = v.entry.noOo const rest = {
? [undefined, base] p: base.p.slice(v.entry.separationAtP),
: v.entry.sepOo f: base.f.slice(v.entry.separationAtF),
? [ };
{ type: "PH", ps: { p: "و ", f: "óo`"}}, return [ph, rest];
base, }
] const [ph, rest]: [T.PH | undefined, T.PsString] = v.entry.noOo
: ["آ", "ا"].includes(base.p.charAt(0)) && base.f.charAt(0) === "a" ? [undefined, base]
? [ : v.entry.sepOo
{ type: "PH", ps: { p: "وا", f: "wáa" }}, ? [{ type: "PH", ps: { p: "و ", f: "óo`" } }, base]
removeAStart(base), : ["آ", "ا"].includes(base.p.charAt(0)) && base.f.charAt(0) === "a"
] ? [{ type: "PH", ps: { p: "وا", f: "wáa" } }, removeAStart(base)]
: ["óo", "oo"].includes(base.f.slice(0, 2)) : ["óo", "oo"].includes(base.f.slice(0, 2))
? [ ? [{ type: "PH", ps: { p: "و", f: "wÚ" } }, base]
{ type: "PH", ps: { p: "و", f: "wÚ" }}, : ["ée", "ee"].includes(base.f.slice(0, 2)) && base.p.slice(0, 2) === "ای"
base, ? [
] { type: "PH", ps: { p: "وي", f: "wée" } },
: ["ée", "ee"].includes(base.f.slice(0, 2)) && base.p.slice(0, 2) === "ای" {
? [ p: base.p.slice(2),
{ type: "PH", ps: { p: "وي", f: "wée" }}, f: base.f.slice(2),
{ },
p: base.p.slice(2), ]
f: base.f.slice(2), : ["é", "e"].includes(base.f.slice(0, 2)) && base.p.slice(0, 2) === "اې"
}, ? [
] : ["é", "e"].includes(base.f.slice(0, 2)) && base.p.slice(0, 2) === "اې" { type: "PH", ps: { p: "وي", f: "wé" } },
? [ {
{ type: "PH", ps: { p: "وي", f: "wé" }}, p: base.p.slice(2),
{ f: base.f.slice(1),
p: base.p.slice(2), },
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`" } }, base]
? [ : [{ type: "PH", ps: { p: "و", f: "óo" } }, base];
{ type: "PH", ps: { p: "و", f: "óo`"}}, return [ph, removeAccents(rest)];
base, function removeAStart(ps: T.PsString) {
] : [ return {
{ type: "PH", ps: { p: "و", f: "óo" }}, p: ps.p.slice(1),
base, f: ps.f.slice(ps.f[1] === "a" ? 2 : 1),
]; };
return [ }
ph,
removeAccents(rest),
];
function removeAStart(ps: T.PsString) {
return {
p: ps.p.slice(1),
f: ps.f.slice(ps.f[1] === "a" ? 2 : 1),
};
}
} }
function edulIntransBase(v: T.VerbEntryNoFVars): T.SingleOrLengthOpts<T.PsString[]> { function edulIntransBase(
const base = trimOffPs(makePsString(v.entry.p, v.entry.f), 3, 4); v: T.VerbEntryNoFVars
const long: T.PsString[] = [concatPsString( ): T.SingleOrLengthOpts<T.PsString[]> {
base, const base = trimOffPs(makePsString(v.entry.p, v.entry.f), 3, 4);
{ p: "ېږ", f: "éG" }, const long: T.PsString[] = [concatPsString(base, { p: "ېږ", f: "éG" })];
)]; const short: T.PsString[] | undefined = v.entry.shortIntrans
const short: T.PsString[] | undefined = (v.entry.shortIntrans) ? [base]
? [base] : undefined;
: undefined; return short ? { short, long } : long;
return short ? { short, long } : long;
} }
const kawulSpecialPerfective: T.LengthOptions<T.PsString[]> = { const kawulSpecialPerfective: T.LengthOptions<T.PsString[]> = {
long: [{ p: "کړ", f: "kR" }], long: [{ p: "کړ", f: "kR" }],
short: [{ p: "ک", f: "k" }], short: [{ p: "ک", f: "k" }],
}; };

View File

@ -1,39 +1,35 @@
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";
import { renderVP } from "./render-vp"; import { renderVP } from "./render-vp";
import { import {
getAPsFromBlocks, getAPsFromBlocks,
getComplementFromBlocks, getComplementFromBlocks,
getObjectSelectionFromBlocks, getObjectSelectionFromBlocks,
getPredicateSelectionFromBlocks, getPredicateSelectionFromBlocks,
getSubjectSelectionFromBlocks, getSubjectSelectionFromBlocks,
hasEquativeWithLengths, hasEquativeWithLengths,
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,357 +57,506 @@ 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[];
const psResult = compileEPPs(EP.blocks, EP.kids, EP.omitSubject, blankOut); };
export function compileEP(
EP: T.EPRendered,
combineLengths: true,
blankOut?: BlankoutOptions
): { ps: T.PsString[]; e?: string[] };
export function compileEP(
EP: T.EPRendered,
combineLengths?: boolean,
blankOut?: BlankoutOptions
): { ps: T.SingleOrLengthOpts<T.PsString[]>; e?: string[] } {
const psResult = compileEPPs(EP.blocks, EP.kids, EP.omitSubject, blankOut);
return {
ps: combineLengths ? flattenLengths(psResult) : psResult,
e: compileEnglishEP(EP),
};
}
export function compileVP(
VP: T.VPRendered,
form: T.FormVersion
): { ps: T.SingleOrLengthOpts<T.PsString[]>; e?: string[] };
export function compileVP(
VP: T.VPRendered,
form: T.FormVersion,
combineLengths: true,
blankOut?: BlankoutOptions
): { ps: T.PsString[]; e?: string[] };
export function compileVP(
VP: T.VPRendered,
form: T.FormVersion,
combineLengths?: true,
blankOut?: BlankoutOptions
): { ps: T.SingleOrLengthOpts<T.PsString[]>; e?: string[] } {
// const verb = getVerbFromBlocks(VP.blocks).block;
const psResult = compileVPPs(VP.blocks, VP.kids, form, VP.king, blankOut);
return {
ps: combineLengths ? flattenLengths(psResult) : psResult,
// TODO: English doesn't quite work for dynamic compounds in passive voice
e: /* (verb.voice === "passive" && VP.isCompound === "dynamic") ? undefined : */ compileEnglishVP(
VP
),
};
}
function compileVPPs(
blocks: T.Block[][],
kids: T.Kid[],
form: T.FormVersion,
king: "subject" | "object",
blankOut?: BlankoutOptions
): T.PsString[] {
const subjectPerson =
getSubjectSelectionFromBlocks(blocks).selection.selection.person;
const blocksWKids = putKidsInKidsSection(
filterForVisibleBlocksVP(blocks, form, king),
kids,
!!blankOut?.ba
);
return removeDuplicates(
combineIntoText(blocksWKids, subjectPerson, blankOut)
);
}
function compileEPPs(
blocks: T.Block[][],
kids: T.Kid[],
omitSubject: boolean,
blankOut?: BlankoutOptions
): T.SingleOrLengthOpts<T.PsString[]> {
if (hasEquativeWithLengths(blocks)) {
return { return {
ps: combineLengths ? flattenLengths(psResult) : psResult, long: compileEPPs(
e: compileEnglishEP(EP), specifyEquativeLength(blocks, "long"),
kids,
omitSubject,
blankOut
) as T.PsString[],
short: compileEPPs(
specifyEquativeLength(blocks, "short"),
kids,
omitSubject,
blankOut
) as T.PsString[],
}; };
}
const subjectPerson =
getSubjectSelectionFromBlocks(blocks).selection.selection.person;
const blocksWKids = putKidsInKidsSection(
omitSubject
? blocks.map((blks) =>
blks.filter((b) => b.block.type !== "subjectSelection")
)
: blocks,
kids,
!!blankOut?.kidsSection
);
return removeDuplicates(
combineIntoText(blocksWKids, subjectPerson, blankOut)
);
} }
export function compileVP(VP: T.VPRendered, form: T.FormVersion): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] }; export function filterForVisibleBlocksVP(
export function compileVP(VP: T.VPRendered, form: T.FormVersion, combineLengths: true, blankOut?: BlankoutOptions): { ps: T.PsString[], e?: string [] }; blocks: T.Block[][],
export function compileVP(VP: T.VPRendered, form: T.FormVersion, combineLengths?: true, blankOut?: BlankoutOptions): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] } { form: T.FormVersion,
// const verb = getVerbFromBlocks(VP.blocks).block; king: "subject" | "object"
const psResult = compileVPPs(VP.blocks, VP.kids, form, VP.king, blankOut); ): T.Block[][] {
const servant = king === "object" ? "subject" : "object";
return blocks.map((blks) =>
blks.filter((block) => {
if (form.removeKing) {
if (
(king === "subject" && block.block.type === "subjectSelection") ||
(king === "object" && block.block.type === "objectSelection")
)
return false;
}
if (form.shrinkServant) {
if (
(servant === "subject" && block.block.type === "subjectSelection") ||
(servant === "object" && block.block.type === "objectSelection")
)
return false;
}
if (
block.block.type === "objectSelection" &&
typeof block.block.selection !== "object"
) {
return false;
}
return true;
})
);
}
export function filterForVisibleBlocksEP(
blocks: T.Block[][],
omitSubject: boolean
): T.Block[][] {
if (!omitSubject) return blocks;
return blocks.map((blks) =>
blks.filter(({ block }) => {
if (block.type === "subjectSelection") {
return false;
}
return true;
})
);
}
export function combineIntoText(
piecesWVars: (T.Block | T.Kid | T.PsString)[][],
subjectPerson: T.Person,
blankOut?: BlankoutOptions
): T.PsString[] {
function removeDoubleBlanks(x: T.PsString): T.PsString {
return { return {
ps: combineLengths ? flattenLengths(psResult) : psResult, p: x.p.replace(blank.p + blank.p, blank.p),
// TODO: English doesn't quite work for dynamic compounds in passive voice f: x.f.replace(blank.f + blank.f, blank.f),
e: /* (verb.voice === "passive" && VP.isCompound === "dynamic") ? undefined : */ compileEnglishVP(VP),
}; };
} }
function combine(pieces: (T.Block | T.Kid | T.PsString)[]): T.PsString[] {
function compileVPPs(blocks: T.Block[][], kids: T.Kid[], form: T.FormVersion, king: "subject" | "object", blankOut?: BlankoutOptions): T.PsString[] { const first = pieces[0];
const subjectPerson = getSubjectSelectionFromBlocks(blocks) const next = pieces[1];
.selection.selection.person; const rest = pieces.slice(1);
const blocksWKids = putKidsInKidsSection( // better to do map-reduce
filterForVisibleBlocksVP(blocks, form, king), // map the blocks into monoids [T.PsString] (with appropriate space blocks) and then concat them all together
kids, const firstPs =
!!blankOut?.ba "p" in first
); ? [first]
return removeDuplicates(combineIntoText(blocksWKids, subjectPerson, blankOut)); : (blankOut?.equative &&
} "block" in first &&
first.block.type === "equative") ||
function compileEPPs(blocks: T.Block[][], kids: T.Kid[], omitSubject: boolean, blankOut?: BlankoutOptions): T.SingleOrLengthOpts<T.PsString[]> { (blankOut?.verb && "block" in first && isRenderedVerbB(first)) ||
if (hasEquativeWithLengths(blocks)) { (blankOut?.predicate &&
return { "block" in first &&
long: compileEPPs(specifyEquativeLength(blocks, "long"), kids, omitSubject, blankOut) as T.PsString[], first.block.type === "predicateSelection")
short: compileEPPs(specifyEquativeLength(blocks, "short"), kids, omitSubject, blankOut) as T.PsString[], ? [blank]
}; : blankOut?.ba && "kid" in first && first.kid.type === "ba"
? [kidsBlank]
: blankOut?.negative &&
"block" in first &&
first.block.type === "negative"
? [{ p: "", f: "" }]
: getPsFromPiece(first, subjectPerson);
if (!rest.length) {
return firstPs;
} }
const subjectPerson = getSubjectSelectionFromBlocks(blocks) return combine(rest)
.selection.selection.person; .flatMap((r) =>
const blocksWKids = putKidsInKidsSection( firstPs.map((fPs) =>
omitSubject ? blocks.map(blks => blks.filter(b => b.block.type !== "subjectSelection")) : blocks, concatPsString(
kids, fPs,
!!blankOut?.kidsSection // TODO: this spacing is a mess and not accurate
); !("p" in first) &&
return removeDuplicates(combineIntoText(blocksWKids, subjectPerson, blankOut)); "block" in first &&
first.block.type === "PH" &&
!("p" in next) &&
(("block" in next &&
(isRenderedVerbB(next) || next.block.type === "negative")) ||
("kid" in next && next.kid.type === "mini-pronoun"))
? ("block" in next && next.block.type === "negative") ||
("kid" in next && next.kid.type === "mini-pronoun")
? { p: "", f: " " }
: ""
: " ",
r
)
)
)
.map(removeDoubleBlanks);
}
return piecesWVars.flatMap(combine);
} }
export function filterForVisibleBlocksVP(blocks: T.Block[][], form: T.FormVersion, king: "subject" | "object"): T.Block[][] { function getPsFromPiece(
const servant = king === "object" ? "subject" : "object"; piece: T.Block | T.Kid,
return blocks.map(blks => blks.filter((block) => { subjectPerson: T.Person
if (form.removeKing) { ): T.PsString[] {
if ( if ("block" in piece) {
(king === "subject" && block.block.type === "subjectSelection") if (piece.block.type === "negative") {
|| return [
(king === "object" && block.block.type === "objectSelection") negativeParticle[
) return false; piece.block.imperative ? "imperative" : "nonImperative"
} ],
if (form.shrinkServant) { ];
if (
(servant === "subject" && block.block.type === "subjectSelection")
||
(servant === "object" && block.block.type === "objectSelection")
) return false;
}
if (block.block.type === "objectSelection" && typeof block.block.selection !== "object") {
return false;
}
return true;
}));
}
export function filterForVisibleBlocksEP(blocks: T.Block[][], omitSubject: boolean): T.Block[][] {
if (!omitSubject) return blocks;
return blocks.map(blks => blks.filter(({ block }) => {
if (block.type === "subjectSelection") {
return false;
}
return true;
}));
}
function combineIntoText(piecesWVars: (T.Block | T.Kid | T.PsString)[][], subjectPerson: T.Person, blankOut?: BlankoutOptions): T.PsString[] {
function removeDoubleBlanks(x: T.PsString): T.PsString {
return {
p: x.p.replace(blank.p+blank.p, blank.p),
f: x.f.replace(blank.f+blank.f, blank.f),
};
} }
function combine(pieces: (T.Block | T.Kid | T.PsString)[]): T.PsString[] { if (piece.block.type === "equative") {
const first = pieces[0]; // length will already be specified in compileEPPs - this is just for type safety
const next = pieces[1]; return getLong(piece.block.equative.ps);
const rest = pieces.slice(1);
const firstPs = ("p" in first)
? [first]
: (
(blankOut?.equative && "block" in first && first.block.type === "equative") ||
(blankOut?.verb && "block" in first && isRenderedVerbB(first)) ||
(blankOut?.predicate && "block" in first && first.block.type === "predicateSelection")
)
? [blank]
: ((blankOut?.ba) && "kid" in first && first.kid.type === "ba")
? [kidsBlank]
: (blankOut?.negative && "block" in first && first.block.type === "negative")
? [{ p: "", f: "" }]
: getPsFromPiece(first, subjectPerson);
if (!rest.length) {
return firstPs;
}
return combine(rest).flatMap(r => (
firstPs.map(fPs => concatPsString(
fPs,
// TODO: this spacing is a mess and not accurate
(!("p" in first) && "block" in first && first.block.type === "PH" && !("p" in next) && (("block" in next && (isRenderedVerbB(next) || next.block.type === "negative")) || ("kid" in next && next.kid.type === "mini-pronoun")))
? ((("block" in next && next.block.type === "negative") || ("kid" in next && next.kid.type === "mini-pronoun")) ? { p: "", f: " " } : "")
: " ",
r,
))
)
).map(removeDoubleBlanks);
} }
return piecesWVars.flatMap(combine); if (
} piece.block.type === "subjectSelection" ||
piece.block.type === "predicateSelection"
function getPsFromPiece(piece: T.Block | T.Kid, subjectPerson: T.Person): T.PsString[] { ) {
if ("block" in piece) { return getPashtoFromRendered(piece.block.selection, subjectPerson);
if (piece.block.type === "negative") {
return [
negativeParticle[piece.block.imperative ? "imperative" : "nonImperative"],
];
}
if (piece.block.type === "equative") {
// length will already be specified in compileEPPs - this is just for type safety
return getLong(piece.block.equative.ps);
}
if (piece.block.type === "subjectSelection" || piece.block.type === "predicateSelection") {
return getPashtoFromRendered(piece.block.selection, subjectPerson);
}
if (piece.block.type === "AP") {
return getPashtoFromRendered(piece.block, subjectPerson);
}
if (piece.block.type === "PH") {
return [piece.block.ps];
}
if (piece.block.type === "VB") {
return flattenLengths(piece.block.ps);
}
if (piece.block.type === "objectSelection") {
if (typeof piece.block.selection !== "object") {
return [{ p: "", f: "" }];
}
return getPashtoFromRendered(piece.block.selection, subjectPerson);
}
if (piece.block.type === "NComp") {
return [piece.block.comp.ps];
}
if (piece.block.type === "complement") {
if (piece.block.selection.type === "sandwich") {
// TODO: Kinda cheating
return getPashtoFromRendered({ type: "AP", selection: piece.block.selection }, false);
}
return piece.block.selection.ps;
}
// welded
return getPsFromWelded(piece.block);
} }
if ("kid" in piece) { if (piece.block.type === "AP") {
if (piece.kid.type === "ba") { return getPashtoFromRendered(piece.block, subjectPerson);
return [grammarUnits.baParticle];
}
if (piece.kid.type === "mini-pronoun") {
return [piece.kid.ps];
}
} }
throw new Error("unrecognized piece type"); if (piece.block.type === "PH") {
return [piece.block.ps];
}
if (piece.block.type === "VB") {
return flattenLengths(piece.block.ps);
}
if (piece.block.type === "objectSelection") {
if (typeof piece.block.selection !== "object") {
return [{ p: "", f: "" }];
}
return getPashtoFromRendered(piece.block.selection, subjectPerson);
}
if (piece.block.type === "NComp") {
return [piece.block.comp.ps];
}
if (piece.block.type === "complement") {
if (piece.block.selection.type === "sandwich") {
// TODO: Kinda cheating
return getPashtoFromRendered(
{ type: "AP", selection: piece.block.selection },
false
);
}
return piece.block.selection.ps;
}
// welded
return getPsFromWelded(piece.block);
}
if ("kid" in piece) {
if (piece.kid.type === "ba") {
return [grammarUnits.baParticle];
}
if (piece.kid.type === "mini-pronoun") {
return [piece.kid.ps];
}
}
throw new Error("unrecognized piece type");
} }
function getPsFromWelded(v: T.Welded): T.PsString[] { function getPsFromWelded(v: T.Welded): T.PsString[] {
function getPsFromSide(v: T.VBBasic | T.Welded | T.NComp | T.VBGenNum): T.PsString[] { function getPsFromSide(
if (v.type === "VB") { v: T.VBBasic | T.Welded | T.NComp | T.VBGenNum
return flattenLengths(v.ps); ): T.PsString[] {
} if (v.type === "VB") {
if (v.type === "NComp") { return flattenLengths(v.ps);
return [v.comp.ps];
}
return getPsFromWelded(v);
} }
const left = getPsFromSide(v.left); if (v.type === "NComp") {
const right = getPsFromSide(v.right); return [v.comp.ps];
return left.flatMap(leftVar => ( }
right.flatMap(rightVar => ( return getPsFromWelded(v);
concatPsString(leftVar, " ", rightVar) }
)) const left = getPsFromSide(v.left);
)); const right = getPsFromSide(v.right);
return left.flatMap((leftVar) =>
right.flatMap((rightVar) => concatPsString(leftVar, " ", rightVar))
);
} }
function getEngAPs(blocks: T.Block[][]): string { function getEngAPs(blocks: T.Block[][]): string {
return getAPsFromBlocks(blocks).reduce((accum, curr) => { return getAPsFromBlocks(blocks).reduce((accum, curr) => {
const e = getEnglishFromRendered(curr); const e = getEnglishFromRendered(curr);
if (!e) return accum; if (!e) return accum;
return `${accum} ${e}`; return `${accum} ${e}`;
}, ""); }, "");
} }
function getEngComplement(blocks: T.Block[][]): string | undefined { function getEngComplement(blocks: T.Block[][]): string | undefined {
const comp = getComplementFromBlocks(blocks); const comp = getComplementFromBlocks(blocks);
if (!comp) return undefined; if (!comp) return undefined;
if (comp.selection.type === "unselected") { if (comp.selection.type === "unselected") {
return "____"; return "____";
} }
if (comp.selection.type === "sandwich") { if (comp.selection.type === "sandwich") {
return getEnglishFromRendered({ type: "AP", selection: comp.selection }); return getEnglishFromRendered({ type: "AP", selection: comp.selection });
} }
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(
function insert(blocks: T.Block[]): (T.Block | T.Kid | T.PsString)[] { blocksWVars: T.Block[][],
const first = blocks[0]; kids: T.Kid[],
const rest = blocks.slice(1); enforceKidsSectionBlankout: boolean
return [ ): (T.Block | T.Kid | T.PsString)[][] {
first, function insert(blocks: T.Block[]): (T.Block | T.Kid | T.PsString)[] {
...enforceKidsSectionBlankout ? [kidsBlank] : kids, const first = blocks[0];
...rest, const rest = blocks.slice(1);
]; return [
} first,
return blocksWVars.map(insert); ...(enforceKidsSectionBlankout ? [kidsBlank] : kids),
...rest,
];
}
return blocksWVars.map(insert);
} }
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,
// add the complement in English if it's an external complement from a helper verb (kawul/kedul) object,
// TODO! APs,
+ (complement /* && isStativeHelper(getVerbFromBlocks(VP.blocks).block.verb))*/ complement,
? ` ${complement}` }: {
: "") subject: string;
+ APs; object?: string;
APs: string;
complement: string | undefined;
} }
const engSubj = getSubjectSelectionFromBlocks(VP.blocks).selection; ): string {
const obj = getObjectSelectionFromBlocks(VP.blocks).selection; return (
const engObj = typeof obj === "object" e.replace("$SUBJ", subject).replace("$OBJ", object || "") +
? obj // add the complement in English if it's an external complement from a helper verb (kawul/kedul)
: (obj === "none" || obj === T.Person.ThirdPlurMale) // TODO!
? "" (complement /* && isStativeHelper(getVerbFromBlocks(VP.blocks).block.verb))*/
: undefined; ? ` ${complement}`
const engAPs = getEngAPs(VP.blocks); : "") +
const engComplement = getEngComplement(VP.blocks); APs
// require all English parts for making the English phrase );
return (VP.englishBase && engSubj && engObj !== undefined) }
? VP.englishBase.map(e => insertEWords(e, { const engSubj = getSubjectSelectionFromBlocks(VP.blocks).selection;
const obj = getObjectSelectionFromBlocks(VP.blocks).selection;
const engObj =
typeof obj === "object"
? obj
: obj === "none" || obj === T.Person.ThirdPlurMale
? ""
: undefined;
const engAPs = getEngAPs(VP.blocks);
const engComplement = getEngComplement(VP.blocks);
// require all English parts for making the English phrase
return VP.englishBase && engSubj && engObj !== undefined
? VP.englishBase
.map((e) =>
insertEWords(e, {
// TODO: make sure we actually have the english // 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) })
: undefined; )
.map(capitalizeFirstLetter)
: 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(
return e.replace("$SUBJ", subject).replace("$PRED", predicate || "") + APs; e: string,
} {
const engSubjR = getSubjectSelectionFromBlocks(EP.blocks).selection; subject,
const engPredR = getPredicateSelectionFromBlocks(EP.blocks).selection; predicate,
const engSubj = getEnglishFromRendered(engSubjR); APs,
const engPred = getEnglishFromRendered(engPredR); }: { subject: string; predicate: string; APs: string }
const engAPs = getEngAPs(EP.blocks); ): string {
// require all English parts for making the English phrase return e.replace("$SUBJ", subject).replace("$PRED", predicate || "") + APs;
const b = (EP.englishBase && engSubj && engPred) }
? EP.englishBase.map(e => insertEWords(e, { const engSubjR = getSubjectSelectionFromBlocks(EP.blocks).selection;
const engPredR = getPredicateSelectionFromBlocks(EP.blocks).selection;
const engSubj = getEnglishFromRendered(engSubjR);
const engPred = getEnglishFromRendered(engPredR);
const engAPs = getEngAPs(EP.blocks);
// require all English parts for making the English phrase
const b =
EP.englishBase && engSubj && engPred
? EP.englishBase.map((e) =>
insertEWords(e, {
subject: engSubj, subject: engSubj,
predicate: engPred, predicate: engPred,
APs: engAPs, APs: engAPs,
})) })
: undefined; )
return b?.map(capitalizeFirstLetter); : undefined;
return b?.map(capitalizeFirstLetter);
} }
export function checkForMiniPronounsError(s: T.EPSelectionState | T.VPSelectionState): undefined | string { export function checkForMiniPronounsError(
function findDuplicateMiniP(mp: T.MiniPronoun[]): T.MiniPronoun | undefined { s: T.EPSelectionState | T.VPSelectionState
const duplicates = mp.filter((item, index) => ( ): undefined | string {
mp.findIndex(m => item.ps.p === m.ps.p) !== index function findDuplicateMiniP(mp: T.MiniPronoun[]): T.MiniPronoun | undefined {
)); const duplicates = mp.filter(
if (duplicates.length === 0) return undefined; (item, index) => mp.findIndex((m) => item.ps.p === m.ps.p) !== index
return duplicates[0]; );
if (duplicates.length === 0) return undefined;
return duplicates[0];
}
const kids = (() => {
if ("predicate" in s) {
const EPS = completeEPSelection(s);
if (!EPS) return undefined;
return renderEP(EPS).kids;
} }
const kids = (() => { const VPS = completeVPSelection(s);
if ("predicate" in s) { if (!VPS) return undefined;
const EPS = completeEPSelection(s); return renderVP(VPS).kids;
if (!EPS) return undefined; })();
return renderEP(EPS).kids; if (!kids) return undefined;
}; const miniPronouns = kids
const VPS = completeVPSelection(s); .filter((x) => x.kid.type === "mini-pronoun")
if (!VPS) return undefined; .map((m) => m.kid) as T.MiniPronoun[];
return renderVP(VPS).kids; if (miniPronouns.length > 2) {
})(); return "can't add another mini-pronoun, there are alread two";
if (!kids) return undefined; }
const miniPronouns = kids.filter(x => x.kid.type === "mini-pronoun").map(m => m.kid) as T.MiniPronoun[]; const duplicateMiniPronoun = findDuplicateMiniP(miniPronouns);
if (miniPronouns.length > 2) { if (duplicateMiniPronoun) {
return "can't add another mini-pronoun, there are alread two"; return `there's already a ${duplicateMiniPronoun.ps.p} - ${duplicateMiniPronoun.ps.f} mini-pronoun in use, can't have two of those`;
} }
const duplicateMiniPronoun = findDuplicateMiniP(miniPronouns); return undefined;
if (duplicateMiniPronoun) {
return `there's already a ${duplicateMiniPronoun.ps.p} - ${duplicateMiniPronoun.ps.f} mini-pronoun in use, can't have two of those`;
}
return undefined;
} }
function findPossesivesInNP(NP: T.Rendered<T.NPSelection> | T.ObjectNP | undefined): T.Rendered<T.NPSelection>[] { function findPossesivesInNP(
if (NP === undefined) return []; NP: T.Rendered<T.NPSelection> | T.ObjectNP | undefined
if (typeof NP !== "object") return []; ): T.Rendered<T.NPSelection>[] {
if (!NP.selection.possesor) return []; if (NP === undefined) return [];
if (NP.selection.adjectives) { if (typeof NP !== "object") return [];
const { adjectives, ...rest } = NP.selection; if (!NP.selection.possesor) return [];
return [ if (NP.selection.adjectives) {
...findPossesivesInAdjectives(adjectives), const { adjectives, ...rest } = NP.selection;
...findPossesivesInNP({ type: "NP", selection: rest }), return [
]; ...findPossesivesInAdjectives(adjectives),
} ...findPossesivesInNP({ type: "NP", selection: rest }),
if (NP.selection.possesor.shrunken) { ];
return [NP.selection.possesor.np]; }
} if (NP.selection.possesor.shrunken) {
return findPossesivesInNP(NP.selection.possesor.np); return [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>[]
...accum, ): T.Rendered<T.NPSelection>[] {
...findPossesivesInAdjective(curr), return a.reduce(
]), [] as T.Rendered<T.NPSelection>[]) (accum, curr): T.Rendered<T.NPSelection>[] => [
...accum,
...findPossesivesInAdjective(curr),
],
[] as T.Rendered<T.NPSelection>[]
);
} }
function findPossesivesInAdjective(a: T.Rendered<T.AdjectiveSelection>): T.Rendered<T.NPSelection>[] { function findPossesivesInAdjective(
if (!a.sandwich) return []; a: T.Rendered<T.AdjectiveSelection>
return findPossesivesInNP(a.sandwich.inside); ): T.Rendered<T.NPSelection>[] {
if (!a.sandwich) return [];
return findPossesivesInNP(a.sandwich.inside);
} }
export function flattenLengths(r: T.SingleOrLengthOpts<T.PsString[] | T.PsString>): T.PsString[] { export function flattenLengths(
if ("long" in r) { r: T.SingleOrLengthOpts<T.PsString[]>
return Object.values(r).flat(); ): T.PsString[] {
if ("long" in r) {
// because we want to use the short shwul and past equatives as the default
if (r.long[0].p.startsWith("شول") || r.long[0].p.startsWith("ول")) {
return [...r.short, ...r.long];
} }
if (Array.isArray(r)) { return [...r.long, ...r.short, ...("mini" in r && r.mini ? r.mini : [])];
return r; }
} return r;
return [r];
} }

View File

@ -1,285 +1,339 @@
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;
const object = getObjectSelection(VP.blocks).selection; const object = getObjectSelection(VP.blocks).selection;
// Sentence Rules Logic // Sentence Rules Logic
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);
); // TODO: more elegant way of handling this type safety
const complementPerson = getPersonFromNP(object ? object : subject) if (kingPerson === undefined) {
// TODO: more elegant way of handling this type safety throw new Error("king of sentance does not exist");
if (kingPerson === undefined) { }
throw new Error("king of sentance does not exist"); const subjectPerson = getPersonFromNP(subject);
const objectPerson = getPersonFromNP(object);
const inflectSubject =
isPast && isTransitive && !isMascSingAnimatePattern4(subject);
const inflectObject = !isPast && isFirstOrSecondPersPronoun(object);
// Render Elements
const firstBlocks = renderVPBlocks(VP.blocks, VP.externalComplement, {
inflectSubject,
inflectObject,
king,
complementPerson,
});
const { vbs, hasBa } = renderVerb({
verb: VP.verb.verb,
tense: VP.verb.tense,
subject: subjectPerson,
object: objectPerson,
voice: VP.verb.voice,
negative: VP.verb.negative,
});
const VBwNeg = insertNegative(
vbs,
VP.verb.negative,
isImperativeTense(VP.verb.tense)
);
// just enter the negative in the verb blocks
return {
type: "VPRendered",
king,
servant,
isPast,
isTransitive,
isCompound: VP.verb.isCompound,
blocks: VBwNeg.map((VBvars) => [...firstBlocks, ...VBvars]),
kids: getVPKids(hasBa, VP.blocks, VP.form, king),
englishBase: renderEnglishVPBase({
subjectPerson,
object: VP.verb.isCompound === "dynamic" ? "none" : object,
vs: VP.verb,
}),
form: VP.form,
whatsAdjustable: whatsAdjustable(VP),
};
}
function getVPKids(
hasBa: boolean,
blocks: T.VPSBlockComplete[],
form: T.FormVersion,
king: "subject" | "object"
): T.Kid[] {
const subject = getSubjectSelection(blocks).selection;
const objectS = getObjectSelection(blocks).selection;
const object = typeof objectS === "object" ? objectS : undefined;
const servantNP = king === "subject" ? object : subject;
const shrunkenServant =
form.shrinkServant && servantNP
? makeKid(shrinkServant(servantNP))
: undefined;
const shrunkenPossesives = findPossesivesToShrink(
removeAbbreviated(blocks, form, king)
).map(makeKid);
return orderKids([
...(hasBa ? [makeKid({ type: "ba" })] : []),
...(shrunkenServant ? [shrunkenServant] : []),
...(shrunkenPossesives ? shrunkenPossesives : []),
]);
}
function removeAbbreviated(
blocks: T.VPSBlockComplete[],
form: T.FormVersion,
king: "subject" | "object"
): T.VPSBlockComplete[] {
return blocks.filter(({ block }) => {
if (block.type === "subjectSelection") {
if (form.shrinkServant && king === "object") return false;
if (form.removeKing && king === "subject") return false;
} }
const subjectPerson = getPersonFromNP(subject); if (block.type === "objectSelection") {
const objectPerson = getPersonFromNP(object); if (form.shrinkServant && king === "subject") return false;
const inflectSubject = isPast && isTransitive && !isMascSingAnimatePattern4(subject); if (form.removeKing && king === "object") return false;
const inflectObject = !isPast && isFirstOrSecondPersPronoun(object);
// Render Elements
const firstBlocks = renderVPBlocks(VP.blocks, VP.externalComplement, {
inflectSubject,
inflectObject,
king,
complementPerson,
});
const { vbs, hasBa } = renderVerb({
verb: VP.verb.verb,
tense: VP.verb.tense,
person: kingPerson,
complementGenNum: personToGenNum(objectPerson || kingPerson),
voice: VP.verb.voice,
negative: VP.verb.negative,
});
const VBwNeg = insertNegative(vbs, VP.verb.negative, isImperativeTense(VP.verb.tense));
// just enter the negative in the verb blocks
return {
type: "VPRendered",
king,
servant,
isPast,
isTransitive,
isCompound: VP.verb.isCompound,
blocks: VBwNeg.map(VBvars => [...firstBlocks, ...VBvars]),
kids: getVPKids(hasBa, VP.blocks, VP.form, king),
englishBase: renderEnglishVPBase({
subjectPerson,
object: VP.verb.isCompound === "dynamic" ? "none" : object,
vs: VP.verb,
}),
form: VP.form,
whatsAdjustable: whatsAdjustable(VP),
};
}
function getVPKids(hasBa: boolean, blocks: T.VPSBlockComplete[], form: T.FormVersion, king: "subject" | "object"): T.Kid[] {
const subject = getSubjectSelection(blocks).selection;
const objectS = getObjectSelection(blocks).selection;
const object = typeof objectS === "object" ? objectS : undefined;
const servantNP = king === "subject" ? object : subject;
const shrunkenServant = (form.shrinkServant && servantNP)
? makeKid(shrinkServant(servantNP))
: undefined;
const shrunkenPossesives = findPossesivesToShrink(removeAbbreviated(blocks, form, king)).map(makeKid);
return orderKids([
...hasBa ? [makeKid({ type: "ba" })] : [],
...shrunkenServant ? [shrunkenServant] : [],
...shrunkenPossesives ? shrunkenPossesives : [],
]);
}
function removeAbbreviated(blocks: T.VPSBlockComplete[], form: T.FormVersion, king: "subject" | "object"): T.VPSBlockComplete[] {
return blocks.filter(({ block }) => {
if (block.type === "subjectSelection") {
if (form.shrinkServant && king === "object") return false;
if (form.removeKing && king === "subject") return false;
}
if (block.type === "objectSelection") {
if (form.shrinkServant && king === "subject") return false;
if (form.removeKing && king === "object") return false;
}
return true;
});
}
function insertNegative(blocks: T.VerbRenderedOutput, negative: boolean, imperative: boolean): T.Block[][] {
if (!negative) {
return [blocks.flat().map(makeBlock)];
};
const blocksA = blocks.flat().map(makeBlock);
const blocksNoAccentA = mapVerbRenderedOutput(removeAccents, blocks).flat().map(makeBlock);
const neg = makeBlock({ type: "negative", imperative });
const nonStandPerfectiveSplit = hasNonStandardPerfectiveSplit(blocks);
if (blocks[1].length === 2) {
// swapped ending with negative for ability and perfect verb forms
if (nonStandPerfectiveSplit) {
return [
insertFromEnd(swapEndingBlocks(blocksA), neg, 2),
insertFromEnd(swapEndingBlocks(blocksA, 2), neg, 3),
insertFromEnd(blocksNoAccentA, neg, 1),
]
}
return [
insertFromEnd(swapEndingBlocks(blocksA), neg, 2),
insertFromEnd(blocksNoAccentA, neg, 1),
];
} }
return true;
});
}
export function insertNegative(
blocks: T.VerbRenderedOutput,
negative: boolean,
imperative: boolean
): T.Block[][] {
if (!negative) {
return [blocks.flat().map(makeBlock)];
}
const blocksA = blocks.flat().map(makeBlock);
const blocksNoAccentA = mapVerbRenderedOutput(removeAccents, blocks)
.flat()
.map(makeBlock);
const neg = makeBlock({ type: "negative", imperative });
const nonStandPerfectiveSplit = hasNonStandardPerfectiveSplit(blocks);
if (blocks[1].length === 2) {
// swapped ending with negative for ability and perfect verb forms
if (nonStandPerfectiveSplit) { if (nonStandPerfectiveSplit) {
return [ return [
insertFromEnd(blocksNoAccentA, neg, 1), insertFromEnd(swapEndingBlocks(blocksA), neg, 2),
insertFromEnd(blocksNoAccentA, neg, 2), insertFromEnd(swapEndingBlocks(blocksA, 2), neg, 3),
]; insertFromEnd(blocksNoAccentA, neg, 1),
} else { ];
return [insertFromEnd(blocksNoAccentA, neg, 1)];
} }
return [
insertFromEnd(swapEndingBlocks(blocksA), neg, 2),
insertFromEnd(blocksNoAccentA, neg, 1),
];
}
if (nonStandPerfectiveSplit) {
return [
insertFromEnd(blocksNoAccentA, neg, 1),
insertFromEnd(blocksNoAccentA, neg, 2),
];
} else {
return [insertFromEnd(blocksNoAccentA, neg, 1)];
}
} }
function swapEndingBlocks<X>(arr: X[], n: number = 1): X[] { function swapEndingBlocks<X>(arr: X[], n: number = 1): X[] {
return [ return [
...arr.slice(0, arr.length - (n + 1)), ...arr.slice(0, arr.length - (n + 1)),
...arr.slice(-n), ...arr.slice(-n),
...arr.slice(-(n + 1), -n), ...arr.slice(-(n + 1), -n),
]; ];
} }
function insertFromEnd<X>(arr: X[], x: X, n: number): X[] { 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 {
if (!ph) { if (!ph) {
return false; return false;
} }
if (ph.type !== "PH") { if (ph.type !== "PH") {
return false; return false;
} }
return !["و", "وا"].includes(ph.ps.p); return !["و", "وا"].includes(ph.ps.p);
} }
function shrinkServant(np: T.NPSelection): T.MiniPronoun { function shrinkServant(np: T.NPSelection): T.MiniPronoun {
const person = getPersonFromNP(np); const person = getPersonFromNP(np);
return { return {
type: "mini-pronoun", type: "mini-pronoun",
person, person,
ps: getMiniPronounPs(person), ps: getMiniPronounPs(person),
source: "servant", source: "servant",
np, np,
}; };
} }
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";
const object = getObjectSelection(blocks); complementPerson: T.Person | undefined;
const subject = getSubjectSelection(blocks); }
const adverbPerson = typeof object.selection === "object" ): T.Block[] {
? getPersonFromNP(object.selection) const object = getObjectSelection(blocks);
: getPersonFromNP(subject.selection); const subject = getSubjectSelection(blocks);
const b = externalComplement ? [...blocks, { block: externalComplement }] : blocks const adverbPerson =
return b.reduce((blocks, { block }): T.Block[] => { typeof object.selection === "object"
if (block.type === "subjectSelection") { ? getPersonFromNP(object.selection)
return [ : getPersonFromNP(subject.selection);
...blocks, const b = externalComplement
makeBlock({ ? [...blocks, { block: externalComplement }]
type: "subjectSelection", : blocks;
selection: renderNPSelection(block.selection, config.inflectSubject, false, "subject", config.king === "subject" ? "king" : "servant", false), return b.reduce((blocks, { block }): T.Block[] => {
}), if (block.type === "subjectSelection") {
]; return [
} ...blocks,
if (block.type === "objectSelection") { makeBlock({
const object = typeof block === "object" ? block.selection : block; type: "subjectSelection",
if (typeof object !== "object") { selection: renderNPSelection(
return [ block.selection,
...blocks, config.inflectSubject,
makeBlock({ false,
type: "objectSelection", "subject",
selection: object, config.king === "subject" ? "king" : "servant",
}), false
]; ),
} }),
const selection = renderNPSelection(object, config.inflectObject, true, "object", config.king === "object" ? "king" : "servant", false); ];
return [
...blocks,
makeBlock({
type: "objectSelection",
selection,
}),
];
}
if (block.type === "AP") {
return [
...blocks,
makeBlock(renderAPSelection(block, adverbPerson ?? 0)),
];
}
return [
...blocks,
makeBlock(
renderComplementSelection(
block,
// just for typesafety // TODO: only include the person if we're doing an adjective
config.complementPerson || T.Person.FirstSingMale,
)),
];
}, [] as T.Block[]);
}
function whatsAdjustable(VP: T.VPSelectionComplete): "both" | "king" | "servant" {
// TODO: intransitive dynamic compounds?
return (VP.verb.isCompound === "dynamic" && VP.verb.transitivity === "transitive")
? (isPastTense(VP.verb.tense) ? "servant" : "king")
: VP.verb.transitivity === "transitive"
? (VP.verb.voice === "active" ? "both" : "king")
: VP.verb.transitivity === "intransitive"
? "king"
// grammTrans
: isPastTense(VP.verb.tense)
? "servant"
: "king";
}
export function getKingAndServant(isPast: boolean, isTransitive: boolean):
{ king: "subject", servant: "object" } |
{ king: "object", servant: "subject" } |
{ king: "subject", servant: undefined } {
if (!isTransitive) {
return { king: "subject", servant: undefined };
} }
return isPast ? { if (block.type === "objectSelection") {
const object = typeof block === "object" ? block.selection : block;
if (typeof object !== "object") {
return [
...blocks,
makeBlock({
type: "objectSelection",
selection: object,
}),
];
}
const selection = renderNPSelection(
object,
config.inflectObject,
true,
"object",
config.king === "object" ? "king" : "servant",
false
);
return [
...blocks,
makeBlock({
type: "objectSelection",
selection,
}),
];
}
if (block.type === "AP") {
return [
...blocks,
makeBlock(renderAPSelection(block, adverbPerson ?? 0)),
];
}
return [
...blocks,
makeBlock(
renderComplementSelection(
block,
// just for typesafety // TODO: only include the person if we're doing an adjective
config.complementPerson || T.Person.FirstSingMale
)
),
];
}, [] as T.Block[]);
}
function whatsAdjustable(
VP: T.VPSelectionComplete
): "both" | "king" | "servant" {
// TODO: intransitive dynamic compounds?
return VP.verb.isCompound === "dynamic" &&
VP.verb.transitivity === "transitive"
? isPastTense(VP.verb.tense)
? "servant"
: "king"
: VP.verb.transitivity === "transitive"
? VP.verb.voice === "active"
? "both"
: "king"
: VP.verb.transitivity === "intransitive"
? "king"
: // grammTrans
isPastTense(VP.verb.tense)
? "servant"
: "king";
}
export function getKingAndServant(
isPast: boolean,
isTransitive: boolean
):
| { king: "subject"; servant: "object" }
| { king: "object"; servant: "subject" }
| { king: "subject"; servant: undefined } {
if (!isTransitive) {
return { king: "subject", servant: undefined };
}
return isPast
? {
king: "object", 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(
if (typeof o !== "object") return false; o: "none" | T.NPSelection | T.Person.ThirdPlurMale
if (o.selection.type !== "pronoun") return false; ): boolean {
return [0,1,2,3,6,7,8,9].includes(o.selection.person); if (typeof o !== "object") return false;
if (o.selection.type !== "pronoun") return false;
return [0, 1, 2, 3, 6, 7, 8, 9].includes(o.selection.person);
} }
function isMascSingAnimatePattern4(np: T.NPSelection): boolean { 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,338 +1,361 @@
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 = [
T.Person.FirstSingMale, T.Person.FirstSingMale,
T.Person.FirstSingFemale, T.Person.FirstSingFemale,
T.Person.FirstPlurMale, T.Person.FirstPlurMale,
T.Person.FirstPlurFemale, T.Person.FirstPlurFemale,
]; ];
const secondPeople = [ const secondPeople = [
T.Person.SecondSingMale, T.Person.SecondSingMale,
T.Person.SecondSingFemale, T.Person.SecondSingFemale,
T.Person.SecondPlurMale, T.Person.SecondPlurMale,
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
if (np === "none") { ): T.Person | undefined;
return undefined; export function getPersonFromNP(
} np: T.NPSelection | T.ObjectNP
if (typeof np === "number") return np; ): T.Person | undefined {
if (np.selection.type === "participle") { if (np === "none") {
return T.Person.ThirdPlurMale; return undefined;
} }
if (np.selection.type === "pronoun") { if (typeof np === "number") return np;
return np.selection.person; if (np.selection.type === "participle") {
} return T.Person.ThirdPlurMale;
return np.selection.number === "plural" }
? (np.selection.gender === "masc" ? T.Person.ThirdPlurMale : T.Person.ThirdPlurFemale) if (np.selection.type === "pronoun") {
: (np.selection.gender === "masc" ? T.Person.ThirdSingMale : T.Person.ThirdSingFemale); return np.selection.person;
}
return np.selection.number === "plural"
? np.selection.gender === "masc"
? T.Person.ThirdPlurMale
: T.Person.ThirdPlurFemale
: np.selection.gender === "masc"
? T.Person.ThirdSingMale
: T.Person.ThirdSingFemale;
} }
export function removeBa(ps: T.PsString): T.PsString { 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(
function verbTenseToModalTense(tn: T.VerbTense): T.AbilityTense { vs: T.VerbSelection
if (tn === "presentVerb") { ): T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense {
return "presentVerbModal"; function verbTenseToModalTense(tn: T.VerbTense): T.AbilityTense {
} if (tn === "presentVerb") {
if (tn === "subjunctiveVerb") { return "presentVerbModal";
return "subjunctiveVerbModal";
}
if (tn === "imperfectiveFuture") {
return "imperfectiveFutureModal";
}
if (tn === "perfectiveFuture") {
return "perfectiveFutureModal";
}
if (tn === "perfectivePast") {
return "perfectivePastModal";
}
if (tn === "imperfectivePast") {
return "imperfectivePastModal";
}
if (tn === "habitualImperfectivePast") {
return "habitualImperfectivePastModal";
}
if (tn === "habitualPerfectivePast") {
return "habitualPerfectivePastModal";
}
throw new Error("can't convert non verbTense to modalTense");
} }
if (vs.tenseCategory === "basic") { if (tn === "subjunctiveVerb") {
return vs.verbTense; return "subjunctiveVerbModal";
} }
if (vs.tenseCategory === "perfect") { if (tn === "imperfectiveFuture") {
return vs.perfectTense; return "imperfectiveFutureModal";
} }
if (vs.tenseCategory === "imperative") { if (tn === "perfectiveFuture") {
return vs.imperativeTense; return "perfectiveFutureModal";
} }
// vs.tenseCategory === "modal" if (tn === "perfectivePast") {
return verbTenseToModalTense(vs.verbTense); return "perfectivePastModal";
}
if (tn === "imperfectivePast") {
return "imperfectivePastModal";
}
if (tn === "habitualImperfectivePast") {
return "habitualImperfectivePastModal";
}
if (tn === "habitualPerfectivePast") {
return "habitualPerfectivePastModal";
}
throw new Error("can't convert non verbTense to modalTense");
}
if (vs.tenseCategory === "basic") {
return vs.verbTense;
}
if (vs.tenseCategory === "perfect") {
return vs.perfectTense;
}
if (vs.tenseCategory === "imperative") {
return vs.imperativeTense;
}
// vs.tenseCategory === "modal"
return verbTenseToModalTense(vs.verbTense);
} }
export function isPastTense(tense: T.Tense): boolean { export function isPastTense(tense: T.Tense): boolean {
if (isPerfectTense(tense)) return true; if (isPerfectTense(tense)) return true;
return tense.toLowerCase().includes("past"); return tense.toLowerCase().includes("past");
} }
export function perfectTenseHasBa(tense: T.PerfectTense): boolean { export function perfectTenseHasBa(tense: T.PerfectTense): boolean {
const withBa: Parameters<typeof perfectTenseHasBa>[0][] = [ const withBa: Parameters<typeof perfectTenseHasBa>[0][] = [
"futurePerfect", "futurePerfect",
"wouldBePerfect", "wouldBePerfect",
"wouldHaveBeenPerfect", "wouldHaveBeenPerfect",
]; ];
return withBa.includes(tense); return withBa.includes(tense);
} }
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<
const subject = getSubjectSelection(vps.blocks).selection; V extends T.VPSelectionState | T.VPSelectionComplete
const object = getObjectSelection(vps.blocks).selection; >(vps: V): V {
if ("tenseCategory" in vps.verb) { const subject = getSubjectSelection(vps.blocks).selection;
if (!subject || !(typeof object === "object") || (vps.verb.tenseCategory === "imperative")) { const object = getObjectSelection(vps.blocks).selection;
return vps; if ("tenseCategory" in vps.verb) {
} if (
return { !subject ||
...vps, !(typeof object === "object") ||
blocks: adjustObjectSelection( vps.verb.tenseCategory === "imperative"
adjustSubjectSelection(vps.blocks, object), ) {
subject, return vps;
),
};
}
if (!subject|| !vps.verb || !(typeof object === "object")) {
return vps;
} }
return { return {
...vps, ...vps,
blocks: adjustObjectSelection( blocks: adjustObjectSelection(
adjustSubjectSelection(vps.blocks, object), adjustSubjectSelection(vps.blocks, object),
subject, subject
), ),
}; };
}
if (!subject || !vps.verb || !(typeof object === "object")) {
return vps;
}
return {
...vps,
blocks: adjustObjectSelection(
adjustSubjectSelection(vps.blocks, object),
subject
),
};
} }
export function completeVPSelection(vps: T.VPSelectionState): T.VPSelectionComplete | undefined { export function completeVPSelection(
if (!VPSBlocksAreComplete(vps.blocks)) { vps: T.VPSelectionState
return undefined; ): T.VPSelectionComplete | undefined {
} if (!VPSBlocksAreComplete(vps.blocks)) {
// necessary for this version on typscript ... return undefined;
const verb: T.VerbSelectionComplete = { }
...vps.verb, // necessary for this version on typscript ...
tense: getTenseFromVerbSelection(vps.verb), const verb: T.VerbSelectionComplete = {
}; ...vps.verb,
return { tense: getTenseFromVerbSelection(vps.verb),
...vps, };
verb, return {
blocks: vps.blocks, ...vps,
}; verb,
blocks: vps.blocks,
};
} }
export function isThirdPerson(p: T.Person): boolean { export function isThirdPerson(p: T.Person): boolean {
return ( return (
p === T.Person.ThirdSingMale || p === T.Person.ThirdSingMale ||
p === T.Person.ThirdSingFemale || p === T.Person.ThirdSingFemale ||
p === T.Person.ThirdPlurMale || p === T.Person.ThirdPlurMale ||
p === T.Person.ThirdPlurFemale p === T.Person.ThirdPlurFemale
); );
} }
export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState): T.VPSelectionState { export function ensure2ndPersSubjPronounAndNoConflict(
const subject = getSubjectSelection(vps.blocks).selection; vps: T.VPSelectionState
const object = getObjectSelection(vps.blocks).selection; ): T.VPSelectionState {
const subjIs2ndPerson = (subject?.selection.type === "pronoun") && isSecondPerson(subject.selection.person); const subject = getSubjectSelection(vps.blocks).selection;
const objIs2ndPerson = (typeof object === "object") const object = getObjectSelection(vps.blocks).selection;
&& (object.selection.type === "pronoun") const subjIs2ndPerson =
&& isSecondPerson(object.selection.person); subject?.selection.type === "pronoun" &&
const default2ndPersSubject: T.NPSelection = { isSecondPerson(subject.selection.person);
const objIs2ndPerson =
typeof object === "object" &&
object.selection.type === "pronoun" &&
isSecondPerson(object.selection.person);
const default2ndPersSubject: T.NPSelection = {
type: "NP",
selection: {
type: "pronoun",
distance: "far",
person: T.Person.SecondSingMale,
},
};
function getNon2ndPersPronoun() {
let newObjPerson: T.Person;
do {
newObjPerson = randomNumber(0, 12);
} while (isSecondPerson(newObjPerson));
return newObjPerson;
}
if (subjIs2ndPerson && !objIs2ndPerson) {
return vps;
}
if (subjIs2ndPerson && objIs2ndPerson) {
if (typeof object !== "object" || object.selection.type !== "pronoun") {
return vps;
}
return {
...vps,
blocks: adjustObjectSelection(vps.blocks, {
type: "NP", type: "NP",
selection: { selection: {
type: "pronoun", ...object.selection,
distance: "far", person: getNon2ndPersPronoun(),
person: T.Person.SecondSingMale,
}, },
}),
}; };
function getNon2ndPersPronoun() { }
let newObjPerson: T.Person; if (!subjIs2ndPerson && objIs2ndPerson) {
do { if (typeof object !== "object" || object.selection.type !== "pronoun") {
newObjPerson = randomNumber(0, 12); return {
} while(isSecondPerson(newObjPerson)); ...vps,
return newObjPerson; blocks: adjustSubjectSelection(vps.blocks, default2ndPersSubject),
};
} }
if (subjIs2ndPerson && !objIs2ndPerson) { return {
return vps; ...vps,
} blocks: adjustObjectSelection(
if (subjIs2ndPerson && objIs2ndPerson) { adjustSubjectSelection(vps.blocks, default2ndPersSubject),
if (typeof object !== "object" || object.selection.type !== "pronoun") { {
return vps; type: "NP",
selection: {
...object.selection,
person: getNon2ndPersPronoun(),
},
} }
return { ),
...vps, };
blocks: adjustObjectSelection(vps.blocks, { }
type: "NP", if (!subjIs2ndPerson && !objIs2ndPerson) {
selection: { return {
...object.selection, ...vps,
person: getNon2ndPersPronoun(), blocks: adjustSubjectSelection(vps.blocks, default2ndPersSubject),
}, };
}), }
}; throw new Error("error ensuring compatible VPSelection for imperative verb");
} }
if (!subjIs2ndPerson && objIs2ndPerson) {
if (typeof object !== "object" || object.selection.type !== "pronoun") {
return {
...vps,
blocks: adjustSubjectSelection(vps.blocks, default2ndPersSubject)
};
}
return {
...vps,
blocks: adjustObjectSelection(
adjustSubjectSelection(vps.blocks, default2ndPersSubject),
{
type: "NP",
selection: {
...object.selection,
person: getNon2ndPersPronoun(),
},
},
),
};
}
if (!subjIs2ndPerson && !objIs2ndPerson) {
return {
...vps,
blocks: adjustSubjectSelection(vps.blocks, default2ndPersSubject),
};
}
throw new Error("error ensuring compatible VPSelection for imperative verb");
}

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"