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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,14 +6,11 @@
*
*/
import { CSSProperties, useState } from "react";
import {
CSSProperties,
useState,
} from "react";
import {
pickPersInf,
hasPersInfs,
noPersInfs,
pickPersInf,
hasPersInfs,
noPersInfs,
} from "../../../lib/src/misc-helpers";
import VerbInfoItemDisplay from "./VerbInfoItemDisplay";
import PersonInfsPicker from "../PersInfsPicker";
@ -26,56 +23,75 @@ import fadedTree from "./faded-tree.svg";
// import video from "../icons/camera-video-fill";
const indentR = {
paddingLeft: "1rem",
paddingLeft: "1rem",
};
const highlight = {
background: "rgba(255, 227, 10, 0.6)",
background: "rgba(255, 227, 10, 0.6)",
};
const title: CSSProperties = {
fontWeight: "bolder",
marginBottom: "0.5rem",
marginTop: "0.5rem",
fontWeight: "bolder",
marginBottom: "0.5rem",
marginTop: "0.5rem",
};
export function RootsAndStems({ textOptions, info, hidePastParticiple, highlighted, noTails }: {
textOptions: T.TextOptions,
info: T.NonComboVerbInfo | T.PassiveRootsAndStems | T.AbilityRootsAndStems,
hidePastParticiple?: boolean,
highlighted?: T.RootsOrStemsToHighlight,
noTails?: boolean,
export function RootsAndStems({
textOptions,
info,
hidePastParticiple,
highlighted,
noTails,
}: {
textOptions: T.TextOptions;
info: T.NonComboVerbInfo | T.PassiveRootsAndStems | T.AbilityRootsAndStems;
hidePastParticiple?: boolean;
highlighted?: T.RootsOrStemsToHighlight;
noTails?: boolean;
}) {
const hasPerfectiveSplit = ("perfectiveSplit" in info.root && "perfectiveSplit" in info.stem)
&& !!(info.root.perfectiveSplit || info.stem.perfectiveSplit);
const showPersInf = hasPersInfs(info);
const [persInf, setPersInf] = useState<T.PersonInflectionsField>("mascSing");
const [split, setSplit] = useState<boolean>(false);
const perfectiveStem = (info.stem.perfectiveSplit && split)
? info.stem.perfectiveSplit
: info.stem.perfective;
const perfectiveRoot = (info.root.perfectiveSplit && split)
? info.root.perfectiveSplit
: info.root.perfective;
const colClass = "col col-md-5 px-0 mb-1";
const rowClass = "row justify-content-between";
return (
<div className="mt-3">
{showPersInf && <PersonInfsPicker
persInf={persInf}
handleChange={(p) => setPersInf(p)}
transitivity={"transitivity" in info ? info.transitivity : "intransitive"}
/>}
<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={{
const hasPerfectiveSplit =
"perfectiveSplit" in info.root &&
"perfectiveSplit" in info.stem &&
!!(info.root.perfectiveSplit || info.stem.perfectiveSplit);
const showPersInf = hasPersInfs(info);
const [persInf, setPersInf] = useState<T.PersonInflectionsField>("mascSing");
const [split, setSplit] = useState<boolean>(false);
const perfectiveStem =
info.stem.perfectiveSplit && split
? info.stem.perfectiveSplit
: info.stem.perfective;
const perfectiveRoot =
info.root.perfectiveSplit && split
? info.root.perfectiveSplit
: info.root.perfective;
const colClass = "col col-md-5 px-0 mb-1";
const rowClass = "row justify-content-between";
return (
<div className="mt-3">
{showPersInf && (
<PersonInfsPicker
persInf={persInf}
handleChange={(p) => setPersInf(p)}
subjOrObj={
"transitivity" in info && info.transitivity === "intransitive"
? "subject"
: "object"
}
/>
)}
<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",
}}>
{info.def}
@ -85,127 +101,158 @@ export function RootsAndStems({ textOptions, info, hidePastParticiple, highlight
{info.subDef}
</div>
} */}
<div style={{
border: "2px solid black",
padding: "1rem",
margin: "0.25rem",
}} className="container">
<div className={rowClass + " align-items-center"}>
<div className={colClass}>
<i className="fas fa-video fa-lg" />
</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" />
</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
style={{
border: "2px solid black",
padding: "1rem",
margin: "0.25rem",
}}
className="container"
>
<div className={rowClass + " align-items-center"}>
<div className={colClass}>
<i className="fas fa-video fa-lg" />
</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" />
</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>
);
}
function VerbInfo({ info, textOptions, showingStemsAndRoots, toggleShowingSar, highlightInRootsAndStems, hidePastParticiple, hideTypeInfo }: {
info: T.NonComboVerbInfo,
textOptions: T.TextOptions,
showingStemsAndRoots: boolean,
highlightInRootsAndStems?: T.RootsOrStemsToHighlight,
toggleShowingSar: () => void,
hidePastParticiple?: boolean,
hideTypeInfo?: boolean,
function VerbInfo({
info,
textOptions,
showingStemsAndRoots,
toggleShowingSar,
highlightInRootsAndStems,
hidePastParticiple,
hideTypeInfo,
}: {
info: T.NonComboVerbInfo;
textOptions: T.TextOptions;
showingStemsAndRoots: boolean;
highlightInRootsAndStems?: T.RootsOrStemsToHighlight;
toggleShowingSar: () => void;
hidePastParticiple?: boolean;
hideTypeInfo?: boolean;
}) {
const inf = noPersInfs(info.root.imperfective).long;
return (
<div className="my-3">
{!hideTypeInfo && <VerbTypeInfo
info={info}
textOptions={textOptions}
/>}
<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>
);
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 Hider from "../Hider";
import * as T from "../../../types";
import useStickyState from "../useStickyState";
import { conjugateVerb } from "../../../lib/src/verb-conjugation";
import { isImperativeTense } from "../../../lib/src/type-predicates";
// import { conjugateVerb } from "../../../lib/src/verb-conjugation";
function AllTensesDisplay({ VS, opts }: { VS: T.VerbSelection, opts: T.TextOptions }) {
const [showing, setShowing] = useStickyState<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]);
}
function AllTensesDisplay({
VS,
opts,
}: {
VS: T.VerbSelection;
opts: T.TextOptions;
}) {
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
: VS.tenseCategory === "perfect"
? perfectTenseOptions
: VS.tenseCategory === "modal"
? abilityTenseOptions
: imperativeTenseOptions;
const rawConjugations = conjugateVerb(VS.verb.entry, VS.verb.complement);
const conjugations = ("stative" in rawConjugations)
? rawConjugations[VS.isCompound === "stative" ? "stative" : "dynamic"]
: ("transitive" in rawConjugations)
? rawConjugations[VS.transitivity === "grammatically transitive" ? "grammaticallyTransitive" : "transitive"]
: rawConjugations;
function getTense(baseTense: T.VerbTense | T.PerfectTense | T.ImperativeTense): T.VerbTense | T.PerfectTense | T.ImperativeTense | T.AbilityTense {
return VS.tenseCategory === "modal" ? `${baseTense}Modal` as T.AbilityTense : baseTense;
}
return <div>
<div className="clickable mb-2 small text-center" onClick={() => setShowFormulas(x => !x)}>
🧪 {!showFormulas ? "Show" : "Hide"} Formulas
</div>
{options.map((tense) => <div key={Math.random()}>
};
const options =
VS.tenseCategory === "basic"
? verbTenseOptions
: VS.tenseCategory === "perfect"
? perfectTenseOptions
: VS.tenseCategory === "modal"
? abilityTenseOptions
: imperativeTenseOptions;
// const rawConjugations = conjugateVerb(VS.verb.entry, VS.verb.complement);
// const conjugations =
// "stative" in rawConjugations
// ? rawConjugations[VS.isCompound === "stative" ? "stative" : "dynamic"]
// : "transitive" in rawConjugations
// ? rawConjugations[
// VS.transitivity === "grammatically transitive"
// ? "grammaticallyTransitive"
// : "transitive"
// ]
// : rawConjugations;
function getTense(
baseTense: T.VerbTense | T.PerfectTense | T.ImperativeTense
): T.VerbTense | T.PerfectTense | T.ImperativeTense | T.AbilityTense {
return VS.tenseCategory === "modal"
? (`${baseTense}Modal` as T.AbilityTense)
: baseTense;
}
return (
<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
label={tense.label}
showing={showing.includes(tense.value)}
handleChange={() => adjustShowing(tense.value)}
hLevel={5}
label={tense.label}
showing={showing.includes(tense.value)}
handleChange={() => adjustShowing(tense.value)}
hLevel={5}
>
{showFormulas && <div className="mb-1">
<samp>{tense.formula}</samp>
</div>}
{showFormulas && (
<div className="mb-1">
<samp>{tense.formula}</samp>
</div>
)}
{showing && (
<ChartDisplay
conjugations={conjugations}
tense={getTense(tense.value)}
voice={VS.voice}
opts={opts}
verb={VS.verb}
negative={VS.negative}
tense={t}
transitivity={VS.transitivity}
voice={VS.voice}
imperative={isImperativeTense(t)}
opts={opts}
/>
)}
</Hider>
</div>)}
</div>;
</div>
);
})}
</div>
);
}
export default AllTensesDisplay;

View File

@ -0,0 +1,255 @@
import { useEffect, useState } from "react";
import ButtonSelect from "../ButtonSelect";
import { combineIntoText } from "../../../lib/src/phrase-building/compile";
import { insertNegative } from "../../../lib/src/phrase-building/render-vp";
import * as T from "../../../types";
import TableCell from "../TableCell";
import { choosePersInf, getLength } from "../../../lib/src/p-text-helpers";
import genderColors from "../gender-colors";
import { eqPsStringWVars } from "../../../lib/src/fp-ps";
import PersInfsPicker from "../PersInfsPicker";
import { useMediaPredicate } from "react-media-hook";
export const roleIcon = {
king: <i className="mx-1 fas fa-crown" />,
servant: <i className="mx-1 fas fa-male" />,
};
function VerbChartDisplay({
chart,
opts,
shortDefault,
transitivity,
past,
}: {
chart: T.OptionalPersonInflections<
T.SingleOrLengthOpts<T.RenderVerbOutput[]>
>;
opts: T.TextOptions;
shortDefault?: boolean;
transitivity: T.Transitivity;
past: boolean;
}) {
const [length, setLength] = useState<T.Length>(
shortDefault ? "short" : "long"
);
const [persInf, setPersInf] = useState<T.PersonInflectionsField>("mascSing");
useEffect(() => {
setLength("long");
}, [chart]);
const desktop = useMediaPredicate("(min-width: 600px)");
const chartWPers = choosePersInf(chart, persInf);
const chartWLength = getLength(chartWPers, length);
const x = chartWLength.map(renderVerbOutputToText(false));
const verbBlock =
x.length === 12
? [
// 1st pers
[
[x[0], x[6]],
[x[1], x[7]],
],
// 2nd pers
[
[x[2], x[8]],
[x[3], x[9]],
],
// 3rd pers
[
[x[4], x[10]],
[x[5], x[11]],
],
]
: [
// 2nd pers
[
[x[0], x[2]],
[x[1], x[3]],
],
];
return (
<>
<div className="mb-2">
{"mascSing" in chart ? (
<PersInfsPicker
persInf={persInf}
handleChange={setPersInf}
subjOrObj="object"
/>
) : (
<div />
)}
</div>
<div className="row">
<div className="col">
<AgreementInfo transitivity={transitivity} past={past} />
</div>
<div className="col">
{"long" in chartWPers && (
<LengthSelection
hasMini={"mini" in chartWPers}
value={length}
onChange={setLength}
/>
)}
</div>
{desktop && <div className="col" />}
</div>
<table className="table mt-2" style={{ tableLayout: "fixed" }}>
<thead>
<tr>
<th scope="col" style={{ width: "3rem" }}>
Pers.
</th>
<th scope="col">Singular</th>
<th scope="col">Plural</th>
</tr>
</thead>
<tbody>
{verbBlock.map((personRow, i) => (
<PersonRow
key={Math.random()}
// get proper version for imperative blocks
person={verbBlock.length === 1 ? 1 : i}
item={personRow}
opts={opts}
/>
))}
</tbody>
</table>
</>
);
}
function PersonRow({
person,
item,
opts,
}: {
person: number;
item: T.PsString[][][];
opts: T.TextOptions;
}) {
const [singM, singF] = [item[0][0], item[1][0]];
const [plurM, plurF] = [item[0][1], item[1][1]];
// just show one single, ungendered row if the gender doesn't make a difference
if (
eqPsStringWVars.equals(singM, singF) &&
eqPsStringWVars.equals(plurM, plurF)
) {
return (
<GenderedPersonRow
person={person}
line={item[0]}
opts={opts}
gender={undefined}
/>
);
}
return (
<>
<GenderedPersonRow
person={person}
line={item[0]}
opts={opts}
gender="masc"
/>
<GenderedPersonRow
person={person}
line={item[1]}
opts={opts}
gender="fem"
/>
</>
);
}
function GenderedPersonRow({
person,
line,
gender,
opts,
}: {
person: number;
line: T.PsString[][];
gender: T.Gender | undefined;
opts: T.TextOptions;
}) {
const pers = ["1st", "2nd", "3rd"]; // arr.length > 1 ? ["1st", "2nd", "3rd"] : ["2nd"];
const rowLabel = `${pers[person]}${
gender ? (gender === "masc" ? " m." : " f.") : ""
}`;
const color = !gender ? "inherit" : genderColors[gender];
return (
<tr key={`${person}${gender}`}>
<th scope="row" style={{ color }}>
{rowLabel}
</th>
<TableCell item={line[0]} textOptions={opts} />
<TableCell item={line[1]} textOptions={opts} />
</tr>
);
}
function LengthSelection({
value,
onChange,
hasMini,
}: {
hasMini: boolean;
value: T.Length;
onChange: (v: T.Length) => void;
}) {
return (
<div className="text-center">
<ButtonSelect
small
options={[
{ label: "Long", value: "long" },
{ label: "Short", value: "short" },
...(hasMini
? [
{
label: "Mini",
value: "mini",
},
]
: []),
]}
value={value}
handleChange={(p) => onChange(p as T.Length)}
/>
</div>
);
}
function renderVerbOutputToText(negative: boolean) {
return function (v: T.RenderVerbOutput): T.PsString[] {
const blocks = insertNegative(
v.vbs,
negative,
false /* TODO: apply imperative */
);
return combineIntoText(blocks, 0 /* TODO: why is this needed */);
};
}
function AgreementInfo({
transitivity,
past,
}: {
transitivity: T.Transitivity;
past: boolean;
}) {
return (
<div className="text-muted small mt-1">
{roleIcon.king} agrees w/{" "}
<strong>
{transitivity !== "intransitive" && past ? "object" : "subject"}
</strong>
</div>
);
}
export default VerbChartDisplay;

View File

@ -1,24 +1,43 @@
import {
getTenseVerbForm,
} from "../../../lib/src/phrase-building/vp-tools";
import VerbFormDisplay from "../VerbFormDisplay";
import NewVerbFormDisplay from "./NewVerbFormDisplay";
import * as T from "../../../types";
import { buildVerbChart } from "./chart-builder";
import { isPastTense } from "../../library";
function ChartDisplay({ conjugations, tense, opts, voice }: {
conjugations: T.VerbConjugation,
tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense,
opts: T.TextOptions,
voice: T.VerbSelection["voice"],
function ChartDisplay({
verb,
tense,
opts,
voice,
imperative,
negative,
transitivity,
}: {
verb: T.VerbEntry;
tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense;
opts: T.TextOptions;
voice: T.VerbSelection["voice"];
imperative: boolean;
negative: boolean;
transitivity: T.Transitivity;
}) {
const form = getTenseVerbForm(conjugations, tense, voice, "charts", false);
return <div className="mb-4">
<VerbFormDisplay
displayForm={form}
showingFormInfo={false}
textOptions={opts}
info={conjugations.info}
/>
</div>;
const verbChart = buildVerbChart({
verb,
tense,
voice,
negative,
transitivity,
imperative,
});
return (
<div className="mb-4">
<NewVerbFormDisplay
chart={verbChart}
opts={opts}
transitivity={transitivity}
past={isPastTense(tense)}
/>
</div>
);
}
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,8 +10,8 @@ import * as T from "../../types";
import { fmapSingleOrLengthOpts } from "./fmaps";
export const blank: T.PsString = {
p: "_____",
f: "_____",
p: "_____",
f: "_____",
};
export const kidsBlank: T.PsString = { p: "___", f: "___" };
@ -22,38 +22,39 @@ export const kidsBlank: T.PsString = { p: "___", f: "___" };
* @returns DictionaryEntry
*/
export function entryOfFull(e: T.FullEntry): T.DictionaryEntry {
return "entry" in e ? e.entry : e;
return "entry" in e ? e.entry : e;
}
// just for type safety
export function noPersInfs<S extends object>(s: T.OptionalPersonInflections<S>): S {
if ("mascSing" in s) {
// this path shouldn't be used, just for type safety
return s.mascSing;
}
return s;
export function noPersInfs<S extends object>(
s: T.OptionalPersonInflections<S>
): S {
if ("mascSing" in s) {
// this path shouldn't be used, just for type safety
return s.mascSing;
}
return s;
}
export function ensureNonComboVerbInfo(i: T.VerbInfo): T.NonComboVerbInfo {
return "stative" in i
? i.stative
: "transitive" in i
? i.transitive
: i;
return "stative" in i ? i.stative : "transitive" in i ? i.transitive : i;
}
export function pickPersInf<T>(s: T.OptionalPersonInflections<T>, persInf: T.PersonInflectionsField): T {
// @ts-ignore
if ("mascSing" in s) {
return s[persInf];
}
return s;
export function pickPersInf<T>(
s: T.OptionalPersonInflections<T>,
persInf: T.PersonInflectionsField
): T {
// @ts-ignore
if ("mascSing" in s) {
return s[persInf];
}
return s;
}
export function getFirstSecThird(p: T.Person): 1 | 2 | 3 {
if ([0, 1, 6, 7].includes(p)) return 1;
if ([2, 3, 8, 9].includes(p)) return 2;
return 3;
if ([0, 1, 6, 7].includes(p)) return 1;
if ([2, 3, 8, 9].includes(p)) return 2;
return 3;
}
// export function pickPersInf(
@ -82,140 +83,169 @@ export function getFirstSecThird(p: T.Person): 1 | 2 | 3 {
// return s;
// }
export function hasPersInfs(info: T.NonComboVerbInfo | T.PassiveRootsAndStems | T.AbilityRootsAndStems): boolean {
if ("participle" in info) {
return (
"mascSing" in info.root.perfective ||
"mascSing" in info.stem.perfective ||
("present" in info.participle && "mascSing" in info.participle.present) ||
"mascSing" in info.participle.past
);
}
export function hasPersInfs(
info: T.NonComboVerbInfo | T.PassiveRootsAndStems | T.AbilityRootsAndStems
): boolean {
if ("participle" in info) {
return (
"mascSing" in info.root.perfective ||
"mascSing" in info.stem.perfective
"mascSing" in info.root.perfective ||
"mascSing" in info.stem.perfective ||
("present" in info.participle && "mascSing" in info.participle.present) ||
"mascSing" in info.participle.past
);
}
return (
"mascSing" in info.root.perfective || "mascSing" in info.stem.perfective
);
}
// TODO: deprecated using new verb rendering thing
export function chooseParticipleInflection(
pPartInfs: T.SingleOrLengthOpts<T.UnisexInflections> | T.SingleOrLengthOpts<T.PsString>,
person: T.Person,
pPartInfs:
| T.SingleOrLengthOpts<T.UnisexInflections>
| T.SingleOrLengthOpts<T.PsString>,
person: T.Person
): T.SingleOrLengthOpts<T.PsString> {
if ("long" in pPartInfs) {
return {
short: chooseParticipleInflection(pPartInfs.short, person) as T.PsString,
long: chooseParticipleInflection(pPartInfs.long, person) as T.PsString,
};
}
if ("masc" in pPartInfs) {
const gender = personGender(person);
const infNum = personIsPlural(person) ? 1 : 0;
return pPartInfs[gender][infNum][0];
}
return pPartInfs; // already just one thing
if ("long" in pPartInfs) {
return {
short: chooseParticipleInflection(pPartInfs.short, person) as T.PsString,
long: chooseParticipleInflection(pPartInfs.long, person) as T.PsString,
};
}
if ("masc" in pPartInfs) {
const gender = personGender(person);
const infNum = personIsPlural(person) ? 1 : 0;
return pPartInfs[gender][infNum][0];
}
return pPartInfs; // already just one thing
}
export function getPersonNumber(gender: T.Gender, number: T.NounNumber): T.Person {
const base = gender === "masc" ? 4 : 5;
return base + (number === "singular" ? 0 : 6);
export function getPersonNumber(
gender: T.Gender,
number: T.NounNumber
): T.Person {
const base = gender === "masc" ? 4 : 5;
return base + (number === "singular" ? 0 : 6);
}
export function personFromVerbBlockPos(pos: [number, number]): T.Person {
return pos[0] + (pos[1] === 1 ? 6 : 0);
return pos[0] + (pos[1] === 1 ? 6 : 0);
}
export function getPersonInflectionsKey(person: T.Person): T.PersonInflectionsField {
return `${personGender(person)}${personIsPlural(person) ? "Plur" : "Sing"}` as T.PersonInflectionsField;
export function getPersonInflectionsKey(
person: T.Person
): T.PersonInflectionsField {
return `${personGender(person)}${
personIsPlural(person) ? "Plur" : "Sing"
}` as T.PersonInflectionsField;
}
export function spaceInForm(form: T.FullForm<T.PsString>): boolean {
if ("mascSing" in form) {
return spaceInForm(form.mascSing);
}
if ("long" in form) {
return spaceInForm(form.long);
}
return form.p.includes(" ");
if ("mascSing" in form) {
return spaceInForm(form.mascSing);
}
if ("long" in form) {
return spaceInForm(form.long);
}
return form.p.includes(" ");
}
export function getPersonFromVerbForm(form: T.SingleOrLengthOpts<T.VerbBlock>, person: T.Person): T.SentenceForm {
return fmapSingleOrLengthOpts(x => {
const [row, col] = getVerbBlockPosFromPerson(person);
return x[row][col];
}, form);
export function getPersonFromVerbForm(
form: T.SingleOrLengthOpts<T.VerbBlock>,
person: T.Person
): T.SentenceForm {
return fmapSingleOrLengthOpts((x) => {
const [row, col] = getVerbBlockPosFromPerson(person);
return x[row][col];
}, form);
}
export function getVerbBlockPosFromPerson(person: T.Person): [0 | 1 | 2 | 3 | 4 | 5, 0 | 1] {
const plural = personIsPlural(person)
const row = (plural ? (person - 6) : person) as 0 | 1 | 2 | 3 | 4 | 5;
const col = plural ? 1 : 0;
return [row, col];
export function getVerbBlockPosFromPerson(
person: T.Person
): [0 | 1 | 2 | 3 | 4 | 5, 0 | 1] {
const plural = personIsPlural(person);
const row = (plural ? person - 6 : person) as 0 | 1 | 2 | 3 | 4 | 5;
const col = plural ? 1 : 0;
return [row, col];
}
export function getAuxTransitivity(trans: T.Transitivity): "transitive" | "intransitive" {
return trans === "intransitive" ? "intransitive" : "transitive";
export function getAuxTransitivity(
trans: T.Transitivity
): "transitive" | "intransitive" {
return trans === "intransitive" ? "intransitive" : "transitive";
}
export function personGender(person: T.Person): T.Gender {
return person % 2 === 0 ? "masc" : "fem";
return person % 2 === 0 ? "masc" : "fem";
}
export function personNumber(person: T.Person): T.NounNumber {
return personIsPlural(person) ? "plural" : "singular";
return personIsPlural(person) ? "plural" : "singular";
}
export function personIsPlural(person: T.Person): boolean {
return person > 5;
return person > 5;
}
export function getEnglishPersonInfo(person: T.Person, version?: "short" | "long"): string {
const p = ([0,1,6,7].includes(person)
? "1st"
: [2,3,8,9].includes(person)
? "2nd"
: "3rd") + " pers.";
const number = personIsPlural(person) ? "plur" : "sing";
const n = version === "short"
? (number === "plur" ? "pl" : "sg") : number;
const gender = personGender(person);
const g = version === "short"
? (gender === "masc" ? "m" : "f")
: gender;
return `${p} ${n}. ${g}.`;
export function getEnglishPersonInfo(
person: T.Person,
version?: "short" | "long"
): string {
const p =
([0, 1, 6, 7].includes(person)
? "1st"
: [2, 3, 8, 9].includes(person)
? "2nd"
: "3rd") + " pers.";
const number = personIsPlural(person) ? "plur" : "sing";
const n = version === "short" ? (number === "plur" ? "pl" : "sg") : number;
const gender = personGender(person);
const g = version === "short" ? (gender === "masc" ? "m" : "f") : gender;
return `${p} ${n}. ${g}.`;
}
export function getEnglishGenNumInfo(gender: T.Gender, number: T.NounNumber): string {
return `${gender === "masc" ? "masc" : "fem"} ${number === "plural" ? "plur." : "sing."}`;
export function getEnglishGenNumInfo(
gender: T.Gender,
number: T.NounNumber
): string {
return `${gender === "masc" ? "masc" : "fem"} ${
number === "plural" ? "plur." : "sing."
}`;
}
export function personToGenNum(p: T.Person): {
gender: T.Gender,
number: T.NounNumber,
gender: T.Gender;
number: T.NounNumber;
} {
return {
gender: personGender(p),
number: personNumber(p),
};
return {
gender: personGender(p),
number: personNumber(p),
};
}
export function getEnglishParticipleInflection(person: T.Person, version?: "short" | "long"): string {
const number = personIsPlural(person) ? "plural" : "singular";
const n = version === "short"
? (number === "plural" ? "plur." : "sing.") : number;
const gender = personGender(person);
const g = gender;
return `${g}. ${n}`;
export function getEnglishParticipleInflection(
person: T.Person,
version?: "short" | "long"
): string {
const number = personIsPlural(person) ? "plural" : "singular";
const n =
version === "short" ? (number === "plural" ? "plur." : "sing.") : number;
const gender = personGender(person);
const g = gender;
return `${g}. ${n}`;
}
export function randomNumber(minInclusive: number, maxExclusive: number): number {
return Math.floor(Math.random() * (maxExclusive - minInclusive) + minInclusive);
export function randomNumber(
minInclusive: number,
maxExclusive: number
): number {
return Math.floor(
Math.random() * (maxExclusive - minInclusive) + minInclusive
);
}
export function randFromArray<M>(arr: M[]): M {
return arr[
Math.floor(Math.random()*arr.length)
];
export function randFromArray<M>(arr: Readonly<M[]>): M {
return arr[Math.floor(Math.random() * arr.length)];
}
export const isFirstPerson = (p: T.Person) => [0, 1, 6, 7].includes(p);
@ -223,27 +253,33 @@ export const isSecondPerson = (p: T.Person) => [2, 3, 8, 9].includes(p);
export const isThirdPerson = (p: T.Person) => [4, 5, 10, 11].includes(p);
export function incrementPerson(p: T.Person): T.Person {
return (p + 1) % 12;
return (p + 1) % 12;
}
export function isSentenceForm(f: any): boolean {
if ("long" in f) {
return isSentenceForm(f.long);
}
return Array.isArray(f) && "p" in f[0];
if ("long" in f) {
return isSentenceForm(f.long);
}
return Array.isArray(f) && "p" in f[0];
}
export function isNounAdjOrVerb(entry: T.DictionaryEntry): "nounAdj" | "verb" | false {
if (!entry.c) {
return false;
}
if (entry.c.includes("adj.") || entry.c.includes("n. m.") || entry.c.includes("n. f.")) {
return "nounAdj";
}
if (entry.c.slice(0, 3) === "v. ") {
return "verb";
}
export function isNounAdjOrVerb(
entry: T.DictionaryEntry
): "nounAdj" | "verb" | false {
if (!entry.c) {
return false;
}
if (
entry.c.includes("adj.") ||
entry.c.includes("n. m.") ||
entry.c.includes("n. f.")
) {
return "nounAdj";
}
if (entry.c.slice(0, 3) === "v. ") {
return "verb";
}
return false;
}
/**
@ -255,55 +291,54 @@ export function isNounAdjOrVerb(entry: T.DictionaryEntry): "nounAdj" | "verb" |
* @returns
*/
export function parseEc(ec: string): T.EnglishVerbConjugationEc {
function isVowel(s: string): boolean {
return ["a", "e", "i", "o", "u"].includes(s);
function isVowel(s: string): boolean {
return ["a", "e", "i", "o", "u"].includes(s);
}
function makeRegularConjugations(s: string): T.EnglishVerbConjugationEc {
if (s === "get") {
return ["get", "gets", "getting", "got", "gotten"];
}
function makeRegularConjugations(s: string): T.EnglishVerbConjugationEc {
if (s === "get") {
return ["get","gets","getting","got","gotten"];
}
if (s === "become") {
return ["become","becomes","becoming","became","become"];
}
if (s === "make") {
return ["make","makes","making","made","made"];
}
if (s === "have") {
return ["have","has","having","had","had"];
}
if (s === "be") {
return ["am","is","being","was","been"];
}
if ((s.slice(-1) === "y") && !isVowel(s.slice(-2)[0])) {
const b = s.slice(0, -1);
return [`${s}`, `${b}ies`, `${s}ing`, `${b}ied`, `${b}ied`];
}
if (s.slice(-2) === "ss") {
return [`${s}`, `${s}es`, `${s}ing`, `${s}ed`, `${s}ed`];
}
if (s.slice(-2) === "ie" && !isVowel(s.slice(-3)[0])) {
const b = s.slice(0, -2);
return [`${s}`, `${s}s`, `${b}ying`, `${s}d`, `${s}d`];
}
const b = s === ""
? "VERB"
: (s.slice(-1) === "e")
? s.slice(0, -1)
: s;
return [`${s}`, `${s}s`, `${b}ing`, `${b}ed`, `${b}ed`];
if (s === "become") {
return ["become", "becomes", "becoming", "became", "become"];
}
const items = ec.split(",").map(x => x.trim());
return (items.length === 4)
? [items[0], items[1], items[2], items[3], items[3]]
: (items.length === 5)
? [items[0], items[1], items[2], items[3], items[4]]
: makeRegularConjugations(items[0]);
if (s === "make") {
return ["make", "makes", "making", "made", "made"];
}
if (s === "have") {
return ["have", "has", "having", "had", "had"];
}
if (s === "be") {
return ["am", "is", "being", "was", "been"];
}
if (s.slice(-1) === "y" && !isVowel(s.slice(-2)[0])) {
const b = s.slice(0, -1);
return [`${s}`, `${b}ies`, `${s}ing`, `${b}ied`, `${b}ied`];
}
if (s.slice(-2) === "ss") {
return [`${s}`, `${s}es`, `${s}ing`, `${s}ed`, `${s}ed`];
}
if (s.slice(-2) === "ie" && !isVowel(s.slice(-3)[0])) {
const b = s.slice(0, -2);
return [`${s}`, `${s}s`, `${b}ying`, `${s}d`, `${s}d`];
}
const b = s === "" ? "VERB" : s.slice(-1) === "e" ? s.slice(0, -1) : s;
return [`${s}`, `${s}s`, `${b}ing`, `${b}ed`, `${b}ed`];
}
const items = ec.split(",").map((x) => x.trim());
return items.length === 4
? [items[0], items[1], items[2], items[3], items[3]]
: items.length === 5
? [items[0], items[1], items[2], items[3], items[4]]
: makeRegularConjugations(items[0]);
}
export function chooseLength<N>(x: T.SingleOrLengthOpts<N>, length: "long" | "short"): N {
// @ts-ignore
if ("long" in x) {
return x[length];
}
return x;
export function chooseLength<N>(
x: T.SingleOrLengthOpts<N>,
length: "long" | "short"
): N {
// @ts-ignore
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 {
getVerbBlockPosFromPerson,
isSecondPerson,
personGender,
personNumber,
personToGenNum,
getVerbBlockPosFromPerson,
isSecondPerson,
personNumber,
personToGenNum,
} from "../misc-helpers";
import { fmapSingleOrLengthOpts } from "../fmaps";
import { concatPsString, getLength } from "../p-text-helpers";
import {
fmapSingleOrLengthOpts,
} from "../fmaps";
import {
concatPsString,
getLength,
} from "../p-text-helpers";
import {
presentEndings,
pastEndings,
equativeEndings,
imperativeEndings,
presentEndings,
pastEndings,
equativeEndings,
imperativeEndings,
} from "../grammar-units";
import { isKawulVerb, isAbilityTense, isPerfectTense, isTlulVerb, isImperativeTense } from "../type-predicates";
import {
isKawulVerb,
isAbilityTense,
isPerfectTense,
isTlulVerb,
} from "../type-predicates";
import { perfectTenseHasBa } from "../phrase-building/vp-tools";
import { makePsString, removeFVarients } from "../accent-and-ps-utils";
import { getPastParticiple, getRootStem } from "./roots-and-stems";
import { isKedul, perfectTenseToEquative, verbEndingConcat } from "./rs-helpers";
import { accentOnNFromEnd, accentPsSyllable, removeAccents } from "../accent-helpers";
import {
isKedul,
perfectTenseToEquative,
verbEndingConcat,
} from "./rs-helpers";
import {
accentOnNFromEnd,
accentPsSyllable,
removeAccents,
} from "../accent-helpers";
const formulas: Record<T.VerbTense | T.ImperativeTense, {
aspect: T.Aspect,
tenseC: "past" | "present" | "imperative",
hasBa: boolean,
}> = {
"presentVerb": {
aspect: "imperfective",
tenseC: "present",
hasBa: false,
},
"subjunctiveVerb": {
aspect: "imperfective",
tenseC: "present",
hasBa: false,
},
"perfectiveFuture": {
aspect: "perfective",
tenseC: "present",
hasBa: true,
},
"imperfectiveFuture": {
aspect: "imperfective",
tenseC: "present",
hasBa: true,
},
"perfectivePast": {
aspect: "perfective",
tenseC: "past",
hasBa: false,
},
"imperfectivePast": {
aspect: "imperfective",
tenseC: "past",
hasBa: false,
},
"habitualImperfectivePast": {
aspect: "imperfective",
tenseC: "past",
hasBa: true,
},
"habitualPerfectivePast": {
aspect: "perfective",
tenseC: "past",
hasBa: true,
},
"perfectiveImperative": {
aspect: "perfective",
tenseC: "imperative",
hasBa: false,
},
"imperfectiveImperative": {
aspect: "imperfective",
tenseC: "imperative",
hasBa: false,
},
}
const formulas: Record<
T.VerbTense | T.ImperativeTense,
{
aspect: T.Aspect;
tenseC: "past" | "present" | "imperative";
hasBa: boolean;
}
> = {
presentVerb: {
aspect: "imperfective",
tenseC: "present",
hasBa: false,
},
subjunctiveVerb: {
aspect: "perfective",
tenseC: "present",
hasBa: false,
},
perfectiveFuture: {
aspect: "perfective",
tenseC: "present",
hasBa: true,
},
imperfectiveFuture: {
aspect: "imperfective",
tenseC: "present",
hasBa: true,
},
perfectivePast: {
aspect: "perfective",
tenseC: "past",
hasBa: false,
},
imperfectivePast: {
aspect: "imperfective",
tenseC: "past",
hasBa: false,
},
habitualImperfectivePast: {
aspect: "imperfective",
tenseC: "past",
hasBa: true,
},
habitualPerfectivePast: {
aspect: "perfective",
tenseC: "past",
hasBa: true,
},
perfectiveImperative: {
aspect: "perfective",
tenseC: "imperative",
hasBa: false,
},
imperfectiveImperative: {
aspect: "imperfective",
tenseC: "imperative",
hasBa: false,
},
};
// to get the chart of conjugations:
// 1. get the conjugation for all persons
// 2. if transitive present tense, check (or do all the conjugation) the conjugation with all different complement person stuff
// if necessary pull out the object option
//
// to make the verbs displayable for the charts
// - take the output of renderVerb { hasBa, VerbRenderedOutput }
// - filter out a long and short version etc if necessary
// - pass it into combineIntoText
// PROBLEM: how to handle when to specify the object
// present tense
// TODO: problem with laaR - no perfective split
export function renderVerb({ verb, tense: tense, person, voice, negative, complementGenNum }: {
verb: T.VerbEntry,
negative: boolean,
tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense, // TODO: make T.Tense
person: T.Person,
complementGenNum: { gender: T.Gender, number: T.NounNumber },
voice: T.Voice,
}): {
hasBa: boolean,
vbs: T.VerbRenderedOutput,
} {
if (isPerfectTense(tense)) {
return renderPerfectVerb({ verb, tense, voice, person });
}
const { aspect, tenseC, hasBa } = formulas[removeAbility(tense)];
const isPast = tenseC === "past";
const type = isAbilityTense(tense) ? "ability" : "basic";
// #1 get the appropriate root / stem
const [vHead, rest] = getRootStem({
verb,
rs: isPast ? "root" : "stem",
aspect,
voice,
type,
genderNumber: complementGenNum,
// TODO: dynamic and stative compounds
export function renderVerb({
verb,
tense,
subject,
object,
voice,
}: {
verb: T.VerbEntry;
negative: boolean;
tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense; // TODO: make T.Tense
subject: T.Person;
object: T.Person | undefined;
voice: T.Voice;
}): T.RenderVerbOutput {
if (isPerfectTense(tense)) {
return renderPerfectVerb({
verb,
tense,
voice,
person: object ?? subject,
});
// #2 add the verb ending to it
const ending = getEnding(person, tenseC, aspect);
return {
hasBa,
vbs: [
vHead,
addEnding({
rs: rest,
ending,
verb,
person,
pastThird: isPast && person === T.Person.ThirdSingMale,
aspect,
basicForm: type === "basic" && voice === "active",
}),
],
};
}
const { aspect, tenseC, hasBa } = formulas[removeAbility(tense)];
const isPast = tenseC === "past";
const type = isAbilityTense(tense) ? "ability" : "basic";
const transitive = object !== undefined;
const king = transitive && isPast ? object : subject;
// #1 get the appropriate root / stem
const [vHead, rest] = getRootStem({
verb,
rs: isPast ? "root" : "stem",
aspect,
voice,
type,
genderNumber: personToGenNum(transitive ? object : subject),
});
// #2 add the verb ending to it
const ending = getEnding(king, tenseC, aspect);
return {
hasBa,
objComp: undefined,
vbs: [
vHead,
addEnding({
rs: rest,
ending,
verb,
person: king,
pastThird: isPast && king === T.Person.ThirdSingMale,
aspect,
basicForm: type === "basic" && voice === "active",
}),
],
};
}
function renderPerfectVerb({ tense, verb, voice, person }: {
tense: T.PerfectTense,
verb: T.VerbEntry,
voice: T.Voice,
person: T.Person,
// TODO: Tighter typing on the output for T.VB (enforce genderNumber?)
}): { hasBa: boolean, vbs: [[], [T.VB, T.VBE]] } {
const hasBa = perfectTenseHasBa(tense);
// #1 get the past participle
const pp = getPastParticiple(verb, voice, personToGenNum(person));
// #2 get the right equative
const equative = equativeEndings[perfectTenseToEquative(tense)];
const [row, col] = getVerbBlockPosFromPerson(person);
const equativeBlock: T.VBE = {
type: "VB",
person,
ps: fmapSingleOrLengthOpts(x => x[row][col], equative),
};
return {
hasBa,
vbs: [[], [pp, equativeBlock]],
};
function renderPerfectVerb({
tense,
verb,
voice,
person,
}: {
person: T.Person;
tense: T.PerfectTense;
verb: T.VerbEntry;
voice: T.Voice;
}): {
hasBa: boolean;
vbs: [[], [T.VB, T.VBE]];
objComp: T.Rendered<T.NPSelection> | undefined;
} {
const hasBa = perfectTenseHasBa(tense);
// #1 get the past participle
const pp = getPastParticiple(verb, voice, personToGenNum(person));
// #2 get the right equative
const equative = equativeEndings[perfectTenseToEquative(tense)];
const [row, col] = getVerbBlockPosFromPerson(person);
const equativeBlock: T.VBE = {
type: "VB",
person,
ps: fmapSingleOrLengthOpts((x) => x[row][col], equative),
};
return {
hasBa,
objComp: undefined,
vbs: [[], [pp, equativeBlock]],
};
}
function addEnding({ verb, rs, ending, person, pastThird, aspect, basicForm }: {
rs: [T.VB, T.VBA] | [T.VBA],
ending: T.SingleOrLengthOpts<T.PsString[]>,
person: T.Person,
verb: T.VerbEntry,
pastThird: boolean,
aspect: T.Aspect,
basicForm: boolean,
function addEnding({
verb,
rs,
ending,
person,
pastThird,
aspect,
basicForm,
}: {
rs: [T.VB, T.VBA] | [T.VBA];
ending: T.SingleOrLengthOpts<T.PsString[]>;
person: T.Person;
verb: T.VerbEntry;
pastThird: boolean;
aspect: T.Aspect;
basicForm: boolean;
}): [T.VB, T.VBE] | [T.VBE] {
return rs.length === 2
? [rs[0], addEnd(rs[1], ending)]
: [addEnd(rs[0], ending)];
function addEnd(vba: T.VBA, ending: T.SingleOrLengthOpts<T.PsString[]>): T.VBE {
if (vba.type === "welded") {
return {
...vba,
right: addToVBBasicEnd(vba.right, ending),
person,
};
}
return {
...addToVBBasicEnd(vba, ending),
person,
}
return rs.length === 2
? [rs[0], addEnd(rs[1], ending)]
: [addEnd(rs[0], ending)];
function addEnd(
vba: T.VBA,
ending: T.SingleOrLengthOpts<T.PsString[]>
): T.VBE {
if (vba.type === "welded") {
return {
...vba,
right: addToVBBasicEnd(vba.right, ending),
person,
};
}
function addToVBBasicEnd(vb: T.VBBasic, end: T.SingleOrLengthOpts<T.PsString[]>): T.VBBasic {
if ("long" in vb.ps) {
if (vb.ps.short[0].f === "ghl" && pastThird && basicForm) {
return {
...vb,
ps: [{ p: "غی", f: "ghey" }],
};
}
const endLong = getLength(end, "long");
const endShort = getLength(end, "short");
// TODO: this is hacky
return {
...vb,
ps: {
long: verbEndingConcat(vb.ps.long, endLong),
short: pastThird && basicForm
? ensure3rdPast(vb.ps.short, endShort, verb, aspect)
: verbEndingConcat(vb.ps.short, endShort),
...vb.ps.mini ? {
mini: verbEndingConcat(vb.ps.mini, endShort),
} : {},
},
};
}
/* istanbul ignore next */
if ("long" in end) {
throw new Error("should not be using verb stems with long and short endings");
}
return {
...addToVBBasicEnd(vba, ending),
person,
};
}
function addToVBBasicEnd(
vb: T.VBBasic,
end: T.SingleOrLengthOpts<T.PsString[]>
): T.VBBasic {
if ("long" in vb.ps) {
if (vb.ps.short[0].f === "ghl" && pastThird && basicForm) {
return {
...vb,
ps: verbEndingConcat(vb.ps, end),
...vb,
ps: [{ p: "غی", f: "ghey" }],
};
}
const endLong = getLength(end, "long");
const endShort = getLength(end, "short");
// TODO: this is hacky
return {
...vb,
ps: {
long: verbEndingConcat(vb.ps.long, endLong),
short:
pastThird && basicForm
? ensure3rdPast(vb.ps.short, endShort, verb, aspect)
: verbEndingConcat(vb.ps.short, endShort),
...(vb.ps.mini
? {
mini: verbEndingConcat(vb.ps.mini, endShort),
}
: {}),
},
};
}
/* istanbul ignore next */
if ("long" in end) {
throw new Error(
"should not be using verb stems with long and short endings"
);
}
return {
...vb,
ps: verbEndingConcat(vb.ps, end),
};
}
}
function getEnding(person: T.Person, tenseC: "present" | "past" | "imperative", aspect: T.Aspect) {
if (tenseC === "imperative") {
if (!isSecondPerson(person)) {
throw new Error("imperative forms must be second person");
}
const number = personNumber(person);
const ends = imperativeEndings[0][number === "singular" ? 0 : 1];
return aspect === "imperfective"
? ends.map(e => accentPsSyllable(e))
: ends;
function getEnding(
person: T.Person,
tenseC: "present" | "past" | "imperative",
aspect: T.Aspect
) {
if (tenseC === "imperative") {
if (!isSecondPerson(person)) {
throw new Error("imperative forms must be second person");
}
const [row, col] = getVerbBlockPosFromPerson(person);
return tenseC === "past" ? {
const number = personNumber(person);
const ends = imperativeEndings[0][number === "singular" ? 0 : 1];
return aspect === "imperfective"
? ends.map((e) => accentPsSyllable(e))
: ends;
}
const [row, col] = getVerbBlockPosFromPerson(person);
return tenseC === "past"
? {
long: pastEndings.long[row][col],
short: pastEndings.short[row][col],
} : presentEndings[row][col];
}
: presentEndings[row][col];
}
// TODO: THIS IS PROBABLY SKEEEETCH
function ensure3rdPast(rs: T.PsString[], ending: T.PsString[], verb: T.VerbEntry, aspect: T.Aspect): T.PsString[] {
if (isKedul(verb)) {
return aspect === "perfective"
? [{ p: "شو", f: "sho" }]
: [{ p: "کېده", f: "kedú" }];
}
if (isKawulVerb(verb) && rs[0].p === "کړ") {
return [
{ p: "کړ", f: "kuR" },
{ p: "کړه", f: "kRu" },
{ p: "کړو", f: "kRo" },
];
}
if (isTlulVerb(verb)) {
// should be imperfective at this point
// the perfective غی should already be covered in the function this is coming from
return [{
p: rs[0].p.slice(0, -1) + "ه",
f: rs[0].f.slice(0, -2) + "ú",
}];
}
if (verb.entry.tppp && verb.entry.tppf) {
const tip = removeAccents(verb.entry.separationAtP !== undefined
? makePsString(verb.entry.tppp.slice(verb.entry.separationAtP), verb.entry.tppf.slice(verb.entry.separationAtF))
: makePsString(verb.entry.tppp, verb.entry.tppf));
const aTip = aspect === "imperfective"
? accentOnNFromEnd(tip, 0)
: tip;
return [aTip];
// if it ends in a consonant, the special form will also have another
// variation ending with a ه - u
// const endsInAConsonant = (pashtoConsonants.includes(tip.p.slice(-1)) || tip.f.slice(-1) === "w");
// return [
// aTip,
// ...endsInAConsonant ? [
// ...verbEndingConcat([aTip], [{ p: "ه", f: "u" }, { p: "و", f: "o" }]),
// ] : [],
// ];
}
const endsInAwul = (
(["awul", "awúl"].includes(removeFVarients(verb.entry.f).slice(-4)))
&&
(verb.entry.p.slice(-2) === "ول")
function ensure3rdPast(
rs: T.PsString[],
ending: T.PsString[],
verb: T.VerbEntry,
aspect: T.Aspect
): T.PsString[] {
if (isKedul(verb)) {
return aspect === "perfective"
? [{ p: "شو", f: "sho" }]
: [{ p: "کېده", f: "kedú" }];
}
if (isKawulVerb(verb) && rs[0].p === "کړ") {
return [
{ p: "کړ", f: "kuR" },
{ p: "کړه", f: "kRu" },
{ p: "کړو", f: "kRo" },
];
}
if (isTlulVerb(verb)) {
// should be imperfective at this point
// the perfective غی should already be covered in the function this is coming from
return [
{
p: rs[0].p.slice(0, -1) + "ه",
f: rs[0].f.slice(0, -2) + "ú",
},
{
p: rs[0].p + "و",
f: rs[0].f.slice(0, -1) + "ó",
},
];
}
if (verb.entry.tppp && verb.entry.tppf) {
const tip = removeAccents(
verb.entry.separationAtP !== undefined
? makePsString(
verb.entry.tppp.slice(verb.entry.separationAtP),
verb.entry.tppf.slice(verb.entry.separationAtF)
)
: makePsString(verb.entry.tppp, verb.entry.tppf)
);
// TODO: check about verbs like tawul (if they exist)
if (endsInAwul) {
const base = { p: rs[0].p.slice(0, -1), f: rs[0].f.slice(0, -2) };
const aawuEnd = concatPsString(base, { p: "اوه", f: base.f.charAt(base.f.length-1) === "a" ? "awu" : "aawu" });
return [aspect === "imperfective"
? accentOnNFromEnd(aawuEnd, 0)
: aawuEnd];
}
const endsInDental = ["د", "ت"].includes(rs[0].p.slice(-1));
// short endings like ورسېد
const ends = endsInDental ? [{ p: "", f: "" }, ...ending] : ending;
return verbEndingConcat(rs, ends);
const aTip = aspect === "imperfective" ? accentOnNFromEnd(tip, 0) : tip;
return [aTip];
// if it ends in a consonant, the special form will also have another
// variation ending with a ه - u
// const endsInAConsonant = (pashtoConsonants.includes(tip.p.slice(-1)) || tip.f.slice(-1) === "w");
// return [
// aTip,
// ...endsInAConsonant ? [
// ...verbEndingConcat([aTip], [{ p: "ه", f: "u" }, { p: "و", f: "o" }]),
// ] : [],
// ];
}
const endsInAwul =
["awul", "awúl"].includes(removeFVarients(verb.entry.f).slice(-4)) &&
verb.entry.p.slice(-2) === "ول";
// TODO: check about verbs like tawul (if they exist)
if (endsInAwul) {
const base = { p: rs[0].p.slice(0, -1), f: rs[0].f.slice(0, -2) };
const aawuEnd = concatPsString(base, {
p: "اوه",
f: base.f.charAt(base.f.length - 1) === "a" ? "awu" : "aawu",
});
return [aspect === "imperfective" ? accentOnNFromEnd(aawuEnd, 0) : aawuEnd];
}
const endsInDental = ["د", "ت"].includes(rs[0].p.slice(-1));
// short endings like ورسېد
const ends = endsInDental ? [{ p: "", f: "" }, ...ending] : ending;
return verbEndingConcat(rs, ends);
}
function removeAbility(tense: T.VerbTense | T.AbilityTense | T.ImperativeTense): T.VerbTense | T.ImperativeTense {
return tense.replace("Modal", "") as T.VerbTense | T.ImperativeTense;
function removeAbility(
tense: T.VerbTense | T.AbilityTense | T.ImperativeTense
): T.VerbTense | T.ImperativeTense {
return tense.replace("Modal", "") as T.VerbTense | T.ImperativeTense;
}

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -1,285 +1,339 @@
import * as T from "../../../types";
import {
mapVerbRenderedOutput,
} from "../fmaps";
import { mapVerbRenderedOutput } from "../fmaps";
import { removeAccents } from "../accent-helpers";
import {
getPersonFromNP,
isPastTense,
} from "./vp-tools";
import {
isImperativeTense,
isPattern4Entry,
} from "../type-predicates";
import { getPersonFromNP, isPastTense } from "./vp-tools";
import { isImperativeTense, isPattern4Entry } from "../type-predicates";
import { renderVerb } from "../new-verb-engine/render-verb";
import { renderEnglishVPBase } from "./english-vp-rendering";
import { renderNPSelection } from "./render-np";
import { getObjectSelection, getSubjectSelection, makeBlock, makeKid } from "./blocks-utils";
import {
getObjectSelection,
getSubjectSelection,
makeBlock,
makeKid,
} from "./blocks-utils";
import { renderAPSelection } from "./render-ap";
import { findPossesivesToShrink, orderKids, getMiniPronounPs } from "./render-common";
import {
findPossesivesToShrink,
orderKids,
getMiniPronounPs,
} from "./render-common";
import { renderComplementSelection } from "./render-complement";
import { personToGenNum } from "../misc-helpers";
export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
const subject = getSubjectSelection(VP.blocks).selection;
const object = getObjectSelection(VP.blocks).selection;
// Sentence Rules Logic
const isPast = isPastTense(VP.verb.tense);
const isTransitive = object !== "none";
const { king, servant } = getKingAndServant(isPast, isTransitive);
const kingPerson = getPersonFromNP(
king === "subject" ? subject : object,
);
const complementPerson = getPersonFromNP(object ? object : subject)
// TODO: more elegant way of handling this type safety
if (kingPerson === undefined) {
throw new Error("king of sentance does not exist");
const subject = getSubjectSelection(VP.blocks).selection;
const object = getObjectSelection(VP.blocks).selection;
// Sentence Rules Logic
const isPast = isPastTense(VP.verb.tense);
const isTransitive = object !== "none";
const { king, servant } = getKingAndServant(isPast, isTransitive);
const kingPerson = getPersonFromNP(king === "subject" ? subject : object);
const complementPerson = getPersonFromNP(object ? object : subject);
// TODO: more elegant way of handling this type safety
if (kingPerson === undefined) {
throw new Error("king of sentance does not exist");
}
const subjectPerson = getPersonFromNP(subject);
const objectPerson = getPersonFromNP(object);
const inflectSubject =
isPast && isTransitive && !isMascSingAnimatePattern4(subject);
const inflectObject = !isPast && isFirstOrSecondPersPronoun(object);
// Render Elements
const firstBlocks = renderVPBlocks(VP.blocks, VP.externalComplement, {
inflectSubject,
inflectObject,
king,
complementPerson,
});
const { vbs, hasBa } = renderVerb({
verb: VP.verb.verb,
tense: VP.verb.tense,
subject: subjectPerson,
object: objectPerson,
voice: VP.verb.voice,
negative: VP.verb.negative,
});
const VBwNeg = insertNegative(
vbs,
VP.verb.negative,
isImperativeTense(VP.verb.tense)
);
// just enter the negative in the verb blocks
return {
type: "VPRendered",
king,
servant,
isPast,
isTransitive,
isCompound: VP.verb.isCompound,
blocks: VBwNeg.map((VBvars) => [...firstBlocks, ...VBvars]),
kids: getVPKids(hasBa, VP.blocks, VP.form, king),
englishBase: renderEnglishVPBase({
subjectPerson,
object: VP.verb.isCompound === "dynamic" ? "none" : object,
vs: VP.verb,
}),
form: VP.form,
whatsAdjustable: whatsAdjustable(VP),
};
}
function getVPKids(
hasBa: boolean,
blocks: T.VPSBlockComplete[],
form: T.FormVersion,
king: "subject" | "object"
): T.Kid[] {
const subject = getSubjectSelection(blocks).selection;
const objectS = getObjectSelection(blocks).selection;
const object = typeof objectS === "object" ? objectS : undefined;
const servantNP = king === "subject" ? object : subject;
const shrunkenServant =
form.shrinkServant && servantNP
? makeKid(shrinkServant(servantNP))
: undefined;
const shrunkenPossesives = findPossesivesToShrink(
removeAbbreviated(blocks, form, king)
).map(makeKid);
return orderKids([
...(hasBa ? [makeKid({ type: "ba" })] : []),
...(shrunkenServant ? [shrunkenServant] : []),
...(shrunkenPossesives ? shrunkenPossesives : []),
]);
}
function removeAbbreviated(
blocks: T.VPSBlockComplete[],
form: T.FormVersion,
king: "subject" | "object"
): T.VPSBlockComplete[] {
return blocks.filter(({ block }) => {
if (block.type === "subjectSelection") {
if (form.shrinkServant && king === "object") return false;
if (form.removeKing && king === "subject") return false;
}
const subjectPerson = getPersonFromNP(subject);
const objectPerson = getPersonFromNP(object);
const inflectSubject = isPast && isTransitive && !isMascSingAnimatePattern4(subject);
const inflectObject = !isPast && isFirstOrSecondPersPronoun(object);
// Render Elements
const firstBlocks = renderVPBlocks(VP.blocks, VP.externalComplement, {
inflectSubject,
inflectObject,
king,
complementPerson,
});
const { vbs, hasBa } = renderVerb({
verb: VP.verb.verb,
tense: VP.verb.tense,
person: kingPerson,
complementGenNum: personToGenNum(objectPerson || kingPerson),
voice: VP.verb.voice,
negative: VP.verb.negative,
});
const VBwNeg = insertNegative(vbs, VP.verb.negative, isImperativeTense(VP.verb.tense));
// just enter the negative in the verb blocks
return {
type: "VPRendered",
king,
servant,
isPast,
isTransitive,
isCompound: VP.verb.isCompound,
blocks: VBwNeg.map(VBvars => [...firstBlocks, ...VBvars]),
kids: getVPKids(hasBa, VP.blocks, VP.form, king),
englishBase: renderEnglishVPBase({
subjectPerson,
object: VP.verb.isCompound === "dynamic" ? "none" : object,
vs: VP.verb,
}),
form: VP.form,
whatsAdjustable: whatsAdjustable(VP),
};
}
function getVPKids(hasBa: boolean, blocks: T.VPSBlockComplete[], form: T.FormVersion, king: "subject" | "object"): T.Kid[] {
const subject = getSubjectSelection(blocks).selection;
const objectS = getObjectSelection(blocks).selection;
const object = typeof objectS === "object" ? objectS : undefined;
const servantNP = king === "subject" ? object : subject;
const shrunkenServant = (form.shrinkServant && servantNP)
? makeKid(shrinkServant(servantNP))
: undefined;
const shrunkenPossesives = findPossesivesToShrink(removeAbbreviated(blocks, form, king)).map(makeKid);
return orderKids([
...hasBa ? [makeKid({ type: "ba" })] : [],
...shrunkenServant ? [shrunkenServant] : [],
...shrunkenPossesives ? shrunkenPossesives : [],
]);
}
function removeAbbreviated(blocks: T.VPSBlockComplete[], form: T.FormVersion, king: "subject" | "object"): T.VPSBlockComplete[] {
return blocks.filter(({ block }) => {
if (block.type === "subjectSelection") {
if (form.shrinkServant && king === "object") return false;
if (form.removeKing && king === "subject") return false;
}
if (block.type === "objectSelection") {
if (form.shrinkServant && king === "subject") return false;
if (form.removeKing && king === "object") return false;
}
return true;
});
}
function insertNegative(blocks: T.VerbRenderedOutput, negative: boolean, imperative: boolean): T.Block[][] {
if (!negative) {
return [blocks.flat().map(makeBlock)];
};
const blocksA = blocks.flat().map(makeBlock);
const blocksNoAccentA = mapVerbRenderedOutput(removeAccents, blocks).flat().map(makeBlock);
const neg = makeBlock({ type: "negative", imperative });
const nonStandPerfectiveSplit = hasNonStandardPerfectiveSplit(blocks);
if (blocks[1].length === 2) {
// swapped ending with negative for ability and perfect verb forms
if (nonStandPerfectiveSplit) {
return [
insertFromEnd(swapEndingBlocks(blocksA), neg, 2),
insertFromEnd(swapEndingBlocks(blocksA, 2), neg, 3),
insertFromEnd(blocksNoAccentA, neg, 1),
]
}
return [
insertFromEnd(swapEndingBlocks(blocksA), neg, 2),
insertFromEnd(blocksNoAccentA, neg, 1),
];
if (block.type === "objectSelection") {
if (form.shrinkServant && king === "subject") return false;
if (form.removeKing && king === "object") return false;
}
return true;
});
}
export function insertNegative(
blocks: T.VerbRenderedOutput,
negative: boolean,
imperative: boolean
): T.Block[][] {
if (!negative) {
return [blocks.flat().map(makeBlock)];
}
const blocksA = blocks.flat().map(makeBlock);
const blocksNoAccentA = mapVerbRenderedOutput(removeAccents, blocks)
.flat()
.map(makeBlock);
const neg = makeBlock({ type: "negative", imperative });
const nonStandPerfectiveSplit = hasNonStandardPerfectiveSplit(blocks);
if (blocks[1].length === 2) {
// swapped ending with negative for ability and perfect verb forms
if (nonStandPerfectiveSplit) {
return [
insertFromEnd(blocksNoAccentA, neg, 1),
insertFromEnd(blocksNoAccentA, neg, 2),
];
} else {
return [insertFromEnd(blocksNoAccentA, neg, 1)];
return [
insertFromEnd(swapEndingBlocks(blocksA), neg, 2),
insertFromEnd(swapEndingBlocks(blocksA, 2), neg, 3),
insertFromEnd(blocksNoAccentA, neg, 1),
];
}
return [
insertFromEnd(swapEndingBlocks(blocksA), neg, 2),
insertFromEnd(blocksNoAccentA, neg, 1),
];
}
if (nonStandPerfectiveSplit) {
return [
insertFromEnd(blocksNoAccentA, neg, 1),
insertFromEnd(blocksNoAccentA, neg, 2),
];
} else {
return [insertFromEnd(blocksNoAccentA, neg, 1)];
}
}
function swapEndingBlocks<X>(arr: X[], n: number = 1): X[] {
return [
...arr.slice(0, arr.length - (n + 1)),
...arr.slice(-n),
...arr.slice(-(n + 1), -n),
];
return [
...arr.slice(0, arr.length - (n + 1)),
...arr.slice(-n),
...arr.slice(-(n + 1), -n),
];
}
function insertFromEnd<X>(arr: X[], x: X, n: number): X[] {
if (n === 0) {
return [...arr, x];
}
return [
...arr.slice(0, arr.length - n),
x,
...arr.slice(-n),
];
if (n === 0) {
return [...arr, x];
}
return [...arr.slice(0, arr.length - n), x, ...arr.slice(-n)];
}
function hasNonStandardPerfectiveSplit([[ph]]: T.VerbRenderedOutput): boolean {
if (!ph) {
return false;
}
if (ph.type !== "PH") {
return false;
}
return !["و", "وا"].includes(ph.ps.p);
if (!ph) {
return false;
}
if (ph.type !== "PH") {
return false;
}
return !["و", "وا"].includes(ph.ps.p);
}
function shrinkServant(np: T.NPSelection): T.MiniPronoun {
const person = getPersonFromNP(np);
return {
type: "mini-pronoun",
person,
ps: getMiniPronounPs(person),
source: "servant",
np,
};
const person = getPersonFromNP(np);
return {
type: "mini-pronoun",
person,
ps: getMiniPronounPs(person),
source: "servant",
np,
};
}
function renderVPBlocks(blocks: T.VPSBlockComplete[], externalComplement: T.VPSelectionComplete["externalComplement"], config: {
inflectSubject: boolean,
inflectObject: boolean,
king: "subject" | "object",
complementPerson: T.Person | undefined,
}): T.Block[] {
const object = getObjectSelection(blocks);
const subject = getSubjectSelection(blocks);
const adverbPerson = typeof object.selection === "object"
? getPersonFromNP(object.selection)
: getPersonFromNP(subject.selection);
const b = externalComplement ? [...blocks, { block: externalComplement }] : blocks
return b.reduce((blocks, { block }): T.Block[] => {
if (block.type === "subjectSelection") {
return [
...blocks,
makeBlock({
type: "subjectSelection",
selection: renderNPSelection(block.selection, config.inflectSubject, false, "subject", config.king === "subject" ? "king" : "servant", false),
}),
];
}
if (block.type === "objectSelection") {
const object = typeof block === "object" ? block.selection : block;
if (typeof object !== "object") {
return [
...blocks,
makeBlock({
type: "objectSelection",
selection: object,
}),
];
}
const selection = renderNPSelection(object, config.inflectObject, true, "object", config.king === "object" ? "king" : "servant", false);
return [
...blocks,
makeBlock({
type: "objectSelection",
selection,
}),
];
}
if (block.type === "AP") {
return [
...blocks,
makeBlock(renderAPSelection(block, adverbPerson ?? 0)),
];
}
return [
...blocks,
makeBlock(
renderComplementSelection(
block,
// just for typesafety // TODO: only include the person if we're doing an adjective
config.complementPerson || T.Person.FirstSingMale,
)),
];
}, [] as T.Block[]);
}
function whatsAdjustable(VP: T.VPSelectionComplete): "both" | "king" | "servant" {
// TODO: intransitive dynamic compounds?
return (VP.verb.isCompound === "dynamic" && VP.verb.transitivity === "transitive")
? (isPastTense(VP.verb.tense) ? "servant" : "king")
: VP.verb.transitivity === "transitive"
? (VP.verb.voice === "active" ? "both" : "king")
: VP.verb.transitivity === "intransitive"
? "king"
// grammTrans
: isPastTense(VP.verb.tense)
? "servant"
: "king";
}
export function getKingAndServant(isPast: boolean, isTransitive: boolean):
{ king: "subject", servant: "object" } |
{ king: "object", servant: "subject" } |
{ king: "subject", servant: undefined } {
if (!isTransitive) {
return { king: "subject", servant: undefined };
function renderVPBlocks(
blocks: T.VPSBlockComplete[],
externalComplement: T.VPSelectionComplete["externalComplement"],
config: {
inflectSubject: boolean;
inflectObject: boolean;
king: "subject" | "object";
complementPerson: T.Person | undefined;
}
): T.Block[] {
const object = getObjectSelection(blocks);
const subject = getSubjectSelection(blocks);
const adverbPerson =
typeof object.selection === "object"
? getPersonFromNP(object.selection)
: getPersonFromNP(subject.selection);
const b = externalComplement
? [...blocks, { block: externalComplement }]
: blocks;
return b.reduce((blocks, { block }): T.Block[] => {
if (block.type === "subjectSelection") {
return [
...blocks,
makeBlock({
type: "subjectSelection",
selection: renderNPSelection(
block.selection,
config.inflectSubject,
false,
"subject",
config.king === "subject" ? "king" : "servant",
false
),
}),
];
}
return isPast ? {
if (block.type === "objectSelection") {
const object = typeof block === "object" ? block.selection : block;
if (typeof object !== "object") {
return [
...blocks,
makeBlock({
type: "objectSelection",
selection: object,
}),
];
}
const selection = renderNPSelection(
object,
config.inflectObject,
true,
"object",
config.king === "object" ? "king" : "servant",
false
);
return [
...blocks,
makeBlock({
type: "objectSelection",
selection,
}),
];
}
if (block.type === "AP") {
return [
...blocks,
makeBlock(renderAPSelection(block, adverbPerson ?? 0)),
];
}
return [
...blocks,
makeBlock(
renderComplementSelection(
block,
// just for typesafety // TODO: only include the person if we're doing an adjective
config.complementPerson || T.Person.FirstSingMale
)
),
];
}, [] as T.Block[]);
}
function whatsAdjustable(
VP: T.VPSelectionComplete
): "both" | "king" | "servant" {
// TODO: intransitive dynamic compounds?
return VP.verb.isCompound === "dynamic" &&
VP.verb.transitivity === "transitive"
? isPastTense(VP.verb.tense)
? "servant"
: "king"
: VP.verb.transitivity === "transitive"
? VP.verb.voice === "active"
? "both"
: "king"
: VP.verb.transitivity === "intransitive"
? "king"
: // grammTrans
isPastTense(VP.verb.tense)
? "servant"
: "king";
}
export function getKingAndServant(
isPast: boolean,
isTransitive: boolean
):
| { king: "subject"; servant: "object" }
| { king: "object"; servant: "subject" }
| { king: "subject"; servant: undefined } {
if (!isTransitive) {
return { king: "subject", servant: undefined };
}
return isPast
? {
king: "object",
servant: "subject",
} : {
}
: {
king: "subject",
servant: "object",
};
};
}
function isFirstOrSecondPersPronoun(o: "none" | T.NPSelection | T.Person.ThirdPlurMale): boolean {
if (typeof o !== "object") return false;
if (o.selection.type !== "pronoun") return false;
return [0,1,2,3,6,7,8,9].includes(o.selection.person);
function isFirstOrSecondPersPronoun(
o: "none" | T.NPSelection | T.Person.ThirdPlurMale
): boolean {
if (typeof o !== "object") return false;
if (o.selection.type !== "pronoun") return false;
return [0, 1, 2, 3, 6, 7, 8, 9].includes(o.selection.person);
}
function isMascSingAnimatePattern4(np: T.NPSelection): boolean {
if (np.selection.type !== "noun") {
return false;
}
return isPattern4Entry(np.selection.entry)
&& np.selection.entry.c.includes("anim.")
&& (np.selection.number === "singular")
&& (np.selection.gender === "masc");
if (np.selection.type !== "noun") {
return false;
}
return (
isPattern4Entry(np.selection.entry) &&
np.selection.entry.c.includes("anim.") &&
np.selection.number === "singular" &&
np.selection.gender === "masc"
);
}

View File

@ -1,338 +1,361 @@
import * as T from "../../../types";
import {
concatPsString,
psRemove,
psStringEquals,
} from "../p-text-helpers";
import { isImperativeTense, isPerfectTense } from "../type-predicates";
import { concatPsString, psRemove, psStringEquals } from "../p-text-helpers";
import { isPerfectTense } from "../type-predicates";
import * as grammarUnits from "../grammar-units";
import { isSecondPerson, randomNumber } from "../misc-helpers";
import { adjustObjectSelection, adjustSubjectSelection, getObjectSelection, getSubjectSelection, VPSBlocksAreComplete } from "./blocks-utils";
import {
adjustObjectSelection,
adjustSubjectSelection,
getObjectSelection,
getSubjectSelection,
VPSBlocksAreComplete,
} from "./blocks-utils";
export function isInvalidSubjObjCombo(subj: T.Person, obj: T.Person): boolean {
const firstPeople = [
T.Person.FirstSingMale,
T.Person.FirstSingFemale,
T.Person.FirstPlurMale,
T.Person.FirstPlurFemale,
];
const secondPeople = [
T.Person.SecondSingMale,
T.Person.SecondSingFemale,
T.Person.SecondPlurMale,
T.Person.SecondPlurFemale,
];
return (
(firstPeople.includes(subj) && firstPeople.includes(obj))
||
(secondPeople.includes(subj) && secondPeople.includes(obj))
);
const firstPeople = [
T.Person.FirstSingMale,
T.Person.FirstSingFemale,
T.Person.FirstPlurMale,
T.Person.FirstPlurFemale,
];
const secondPeople = [
T.Person.SecondSingMale,
T.Person.SecondSingFemale,
T.Person.SecondPlurMale,
T.Person.SecondPlurFemale,
];
return (
(firstPeople.includes(subj) && firstPeople.includes(obj)) ||
(secondPeople.includes(subj) && secondPeople.includes(obj))
);
}
/**
* @param conjR
* @param tense
* @param voice
* @param negative
* @returns
*/
export function getTenseVerbForm(
conjR: T.VerbConjugation,
tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense,
voice: "active" | "passive",
mode: "charts" | "phrase-building",
negative: boolean,
): T.VerbForm | T.ImperativeForm {
const conj = (voice === "passive" && conjR.passive) ? conjR.passive : conjR;
if (isImperativeTense(tense)) {
const impPassError = new Error("can't use imperative tenses with passive voice")
if (voice === "passive") {
throw impPassError;
}
if (!conj.imperfective.imperative || !conj.perfective.imperative) throw impPassError;
// charts can't display negative form
return (tense === "perfectiveImperative" && (!negative || mode === "charts"))
? conj.perfective.imperative
: conj.imperfective.imperative;
}
if (tense === "presentVerb") {
return conj.imperfective.nonImperative;
}
if (tense === "subjunctiveVerb") {
return conj.perfective.nonImperative;
}
if (tense === "imperfectiveFuture") {
return conj.imperfective.future;
}
if (tense === "perfectiveFuture") {
return conj.perfective.future;
}
if (tense === "imperfectivePast") {
return conj.imperfective.past;
}
if (tense === "perfectivePast") {
return conj.perfective.past;
}
if (tense === "habitualImperfectivePast") {
return conj.imperfective.habitualPast;
}
if (tense === "habitualPerfectivePast") {
return conj.perfective.habitualPast;
}
if (tense === "presentVerbModal") {
return conj.imperfective.modal.nonImperative;
}
if (tense === "subjunctiveVerbModal") {
return conj.perfective.modal.nonImperative;
}
if (tense === "imperfectiveFutureModal") {
return conj.imperfective.modal.future;
}
if (tense === "perfectiveFutureModal") {
return conj.perfective.modal.future;
}
if (tense === "imperfectivePastModal") {
return conj.imperfective.modal.past;
}
if (tense === "perfectivePastModal") {
return conj.perfective.modal.past;
}
if (tense === "habitualImperfectivePastModal") {
return conj.imperfective.modal.habitualPast;
}
if (tense === "habitualPerfectivePastModal") {
return conj.perfective.modal.habitualPast;
}
if (tense === "presentPerfect") {
return conj.perfect.present;
}
if (tense === "pastPerfect") {
return conj.perfect.past;
}
if (tense === "futurePerfect") {
return conj.perfect.future;
}
if (tense === "habitualPerfect") {
return conj.perfect.habitual;
}
if (tense === "subjunctivePerfect") {
return conj.perfect.subjunctive;
}
if (tense === "wouldBePerfect") {
return conj.perfect.wouldBe;
}
if (tense === "pastSubjunctivePerfect") {
return conj.perfect.pastSubjunctive;
}
if (tense === "wouldHaveBeenPerfect") {
return conj.perfect.wouldHaveBeen;
}
throw new Error("unknown tense");
}
// DEPRECATED
// /**
// * @param conjR
// * @param tense
// * @param voice
// * @param negative
// * @returns
// */
// export function getTenseVerbForm(
// conjR: T.VerbConjugation,
// tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense,
// voice: "active" | "passive",
// mode: "charts" | "phrase-building",
// negative: boolean,
// ): T.VerbForm | T.ImperativeForm {
// const conj = (voice === "passive" && conjR.passive) ? conjR.passive : conjR;
// if (isImperativeTense(tense)) {
// const impPassError = new Error("can't use imperative tenses with passive voice")
// if (voice === "passive") {
// throw impPassError;
// }
// if (!conj.imperfective.imperative || !conj.perfective.imperative) throw impPassError;
// // charts can't display negative form
// return (tense === "perfectiveImperative" && (!negative || mode === "charts"))
// ? conj.perfective.imperative
// : conj.imperfective.imperative;
// }
// if (tense === "presentVerb") {
// return conj.imperfective.nonImperative;
// }
// if (tense === "subjunctiveVerb") {
// return conj.perfective.nonImperative;
// }
// if (tense === "imperfectiveFuture") {
// return conj.imperfective.future;
// }
// if (tense === "perfectiveFuture") {
// return conj.perfective.future;
// }
// if (tense === "imperfectivePast") {
// return conj.imperfective.past;
// }
// if (tense === "perfectivePast") {
// return conj.perfective.past;
// }
// if (tense === "habitualImperfectivePast") {
// return conj.imperfective.habitualPast;
// }
// if (tense === "habitualPerfectivePast") {
// return conj.perfective.habitualPast;
// }
// if (tense === "presentVerbModal") {
// return conj.imperfective.modal.nonImperative;
// }
// if (tense === "subjunctiveVerbModal") {
// return conj.perfective.modal.nonImperative;
// }
// if (tense === "imperfectiveFutureModal") {
// return conj.imperfective.modal.future;
// }
// if (tense === "perfectiveFutureModal") {
// return conj.perfective.modal.future;
// }
// if (tense === "imperfectivePastModal") {
// return conj.imperfective.modal.past;
// }
// if (tense === "perfectivePastModal") {
// return conj.perfective.modal.past;
// }
// if (tense === "habitualImperfectivePastModal") {
// return conj.imperfective.modal.habitualPast;
// }
// if (tense === "habitualPerfectivePastModal") {
// return conj.perfective.modal.habitualPast;
// }
// if (tense === "presentPerfect") {
// return conj.perfect.present;
// }
// if (tense === "pastPerfect") {
// return conj.perfect.past;
// }
// if (tense === "futurePerfect") {
// return conj.perfect.future;
// }
// if (tense === "habitualPerfect") {
// return conj.perfect.habitual;
// }
// if (tense === "subjunctivePerfect") {
// return conj.perfect.subjunctive;
// }
// if (tense === "wouldBePerfect") {
// return conj.perfect.wouldBe;
// }
// if (tense === "pastSubjunctivePerfect") {
// return conj.perfect.pastSubjunctive;
// }
// if (tense === "wouldHaveBeenPerfect") {
// return conj.perfect.wouldHaveBeen;
// }
// throw new Error("unknown tense");
// }
export function getPersonFromNP(np: T.NPSelection): T.Person;
export function getPersonFromNP(np: T.NPSelection | T.ObjectNP): T.Person | undefined;
export function getPersonFromNP(np: T.NPSelection | T.ObjectNP): T.Person | undefined {
if (np === "none") {
return undefined;
}
if (typeof np === "number") return np;
if (np.selection.type === "participle") {
return T.Person.ThirdPlurMale;
}
if (np.selection.type === "pronoun") {
return np.selection.person;
}
return np.selection.number === "plural"
? (np.selection.gender === "masc" ? T.Person.ThirdPlurMale : T.Person.ThirdPlurFemale)
: (np.selection.gender === "masc" ? T.Person.ThirdSingMale : T.Person.ThirdSingFemale);
export function getPersonFromNP(
np: T.NPSelection | T.ObjectNP
): T.Person | undefined;
export function getPersonFromNP(
np: T.NPSelection | T.ObjectNP
): T.Person | undefined {
if (np === "none") {
return undefined;
}
if (typeof np === "number") return np;
if (np.selection.type === "participle") {
return T.Person.ThirdPlurMale;
}
if (np.selection.type === "pronoun") {
return np.selection.person;
}
return np.selection.number === "plural"
? np.selection.gender === "masc"
? T.Person.ThirdPlurMale
: T.Person.ThirdPlurFemale
: np.selection.gender === "masc"
? T.Person.ThirdSingMale
: T.Person.ThirdSingFemale;
}
export function removeBa(ps: T.PsString): T.PsString {
return psRemove(ps, concatPsString(grammarUnits.baParticle, " "));
return psRemove(ps, concatPsString(grammarUnits.baParticle, " "));
}
export function getTenseFromVerbSelection(vs: T.VerbSelection): T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense {
function verbTenseToModalTense(tn: T.VerbTense): T.AbilityTense {
if (tn === "presentVerb") {
return "presentVerbModal";
}
if (tn === "subjunctiveVerb") {
return "subjunctiveVerbModal";
}
if (tn === "imperfectiveFuture") {
return "imperfectiveFutureModal";
}
if (tn === "perfectiveFuture") {
return "perfectiveFutureModal";
}
if (tn === "perfectivePast") {
return "perfectivePastModal";
}
if (tn === "imperfectivePast") {
return "imperfectivePastModal";
}
if (tn === "habitualImperfectivePast") {
return "habitualImperfectivePastModal";
}
if (tn === "habitualPerfectivePast") {
return "habitualPerfectivePastModal";
}
throw new Error("can't convert non verbTense to modalTense");
export function getTenseFromVerbSelection(
vs: T.VerbSelection
): T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense {
function verbTenseToModalTense(tn: T.VerbTense): T.AbilityTense {
if (tn === "presentVerb") {
return "presentVerbModal";
}
if (vs.tenseCategory === "basic") {
return vs.verbTense;
if (tn === "subjunctiveVerb") {
return "subjunctiveVerbModal";
}
if (vs.tenseCategory === "perfect") {
return vs.perfectTense;
if (tn === "imperfectiveFuture") {
return "imperfectiveFutureModal";
}
if (vs.tenseCategory === "imperative") {
return vs.imperativeTense;
if (tn === "perfectiveFuture") {
return "perfectiveFutureModal";
}
// vs.tenseCategory === "modal"
return verbTenseToModalTense(vs.verbTense);
if (tn === "perfectivePast") {
return "perfectivePastModal";
}
if (tn === "imperfectivePast") {
return "imperfectivePastModal";
}
if (tn === "habitualImperfectivePast") {
return "habitualImperfectivePastModal";
}
if (tn === "habitualPerfectivePast") {
return "habitualPerfectivePastModal";
}
throw new Error("can't convert non verbTense to modalTense");
}
if (vs.tenseCategory === "basic") {
return vs.verbTense;
}
if (vs.tenseCategory === "perfect") {
return vs.perfectTense;
}
if (vs.tenseCategory === "imperative") {
return vs.imperativeTense;
}
// vs.tenseCategory === "modal"
return verbTenseToModalTense(vs.verbTense);
}
export function isPastTense(tense: T.Tense): boolean {
if (isPerfectTense(tense)) return true;
return tense.toLowerCase().includes("past");
if (isPerfectTense(tense)) return true;
return tense.toLowerCase().includes("past");
}
export function perfectTenseHasBa(tense: T.PerfectTense): boolean {
const withBa: Parameters<typeof perfectTenseHasBa>[0][] = [
"futurePerfect",
"wouldBePerfect",
"wouldHaveBeenPerfect",
];
return withBa.includes(tense);
const withBa: Parameters<typeof perfectTenseHasBa>[0][] = [
"futurePerfect",
"wouldBePerfect",
"wouldHaveBeenPerfect",
];
return withBa.includes(tense);
}
export function removeDuplicates(psv: T.PsString[]): T.PsString[] {
return psv.filter((ps, i, arr) => (
i === arr.findIndex(t => (
psStringEquals(t, ps)
))
));
return psv.filter(
(ps, i, arr) => i === arr.findIndex((t) => psStringEquals(t, ps))
);
}
export function switchSubjObj<V extends T.VPSelectionState | T.VPSelectionComplete>(vps: V): V {
const subject = getSubjectSelection(vps.blocks).selection;
const object = getObjectSelection(vps.blocks).selection;
if ("tenseCategory" in vps.verb) {
if (!subject || !(typeof object === "object") || (vps.verb.tenseCategory === "imperative")) {
return vps;
}
return {
...vps,
blocks: adjustObjectSelection(
adjustSubjectSelection(vps.blocks, object),
subject,
),
};
}
if (!subject|| !vps.verb || !(typeof object === "object")) {
return vps;
export function switchSubjObj<
V extends T.VPSelectionState | T.VPSelectionComplete
>(vps: V): V {
const subject = getSubjectSelection(vps.blocks).selection;
const object = getObjectSelection(vps.blocks).selection;
if ("tenseCategory" in vps.verb) {
if (
!subject ||
!(typeof object === "object") ||
vps.verb.tenseCategory === "imperative"
) {
return vps;
}
return {
...vps,
blocks: adjustObjectSelection(
adjustSubjectSelection(vps.blocks, object),
subject,
),
...vps,
blocks: adjustObjectSelection(
adjustSubjectSelection(vps.blocks, object),
subject
),
};
}
if (!subject || !vps.verb || !(typeof object === "object")) {
return vps;
}
return {
...vps,
blocks: adjustObjectSelection(
adjustSubjectSelection(vps.blocks, object),
subject
),
};
}
export function completeVPSelection(vps: T.VPSelectionState): T.VPSelectionComplete | undefined {
if (!VPSBlocksAreComplete(vps.blocks)) {
return undefined;
}
// necessary for this version on typscript ...
const verb: T.VerbSelectionComplete = {
...vps.verb,
tense: getTenseFromVerbSelection(vps.verb),
};
return {
...vps,
verb,
blocks: vps.blocks,
};
export function completeVPSelection(
vps: T.VPSelectionState
): T.VPSelectionComplete | undefined {
if (!VPSBlocksAreComplete(vps.blocks)) {
return undefined;
}
// necessary for this version on typscript ...
const verb: T.VerbSelectionComplete = {
...vps.verb,
tense: getTenseFromVerbSelection(vps.verb),
};
return {
...vps,
verb,
blocks: vps.blocks,
};
}
export function isThirdPerson(p: T.Person): boolean {
return (
p === T.Person.ThirdSingMale ||
p === T.Person.ThirdSingFemale ||
p === T.Person.ThirdPlurMale ||
p === T.Person.ThirdPlurFemale
);
return (
p === T.Person.ThirdSingMale ||
p === T.Person.ThirdSingFemale ||
p === T.Person.ThirdPlurMale ||
p === T.Person.ThirdPlurFemale
);
}
export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState): T.VPSelectionState {
const subject = getSubjectSelection(vps.blocks).selection;
const object = getObjectSelection(vps.blocks).selection;
const subjIs2ndPerson = (subject?.selection.type === "pronoun") && isSecondPerson(subject.selection.person);
const objIs2ndPerson = (typeof object === "object")
&& (object.selection.type === "pronoun")
&& isSecondPerson(object.selection.person);
const default2ndPersSubject: T.NPSelection = {
export function ensure2ndPersSubjPronounAndNoConflict(
vps: T.VPSelectionState
): T.VPSelectionState {
const subject = getSubjectSelection(vps.blocks).selection;
const object = getObjectSelection(vps.blocks).selection;
const subjIs2ndPerson =
subject?.selection.type === "pronoun" &&
isSecondPerson(subject.selection.person);
const objIs2ndPerson =
typeof object === "object" &&
object.selection.type === "pronoun" &&
isSecondPerson(object.selection.person);
const default2ndPersSubject: T.NPSelection = {
type: "NP",
selection: {
type: "pronoun",
distance: "far",
person: T.Person.SecondSingMale,
},
};
function getNon2ndPersPronoun() {
let newObjPerson: T.Person;
do {
newObjPerson = randomNumber(0, 12);
} while (isSecondPerson(newObjPerson));
return newObjPerson;
}
if (subjIs2ndPerson && !objIs2ndPerson) {
return vps;
}
if (subjIs2ndPerson && objIs2ndPerson) {
if (typeof object !== "object" || object.selection.type !== "pronoun") {
return vps;
}
return {
...vps,
blocks: adjustObjectSelection(vps.blocks, {
type: "NP",
selection: {
type: "pronoun",
distance: "far",
person: T.Person.SecondSingMale,
...object.selection,
person: getNon2ndPersPronoun(),
},
}),
};
function getNon2ndPersPronoun() {
let newObjPerson: T.Person;
do {
newObjPerson = randomNumber(0, 12);
} while(isSecondPerson(newObjPerson));
return newObjPerson;
}
if (!subjIs2ndPerson && objIs2ndPerson) {
if (typeof object !== "object" || object.selection.type !== "pronoun") {
return {
...vps,
blocks: adjustSubjectSelection(vps.blocks, default2ndPersSubject),
};
}
if (subjIs2ndPerson && !objIs2ndPerson) {
return vps;
}
if (subjIs2ndPerson && objIs2ndPerson) {
if (typeof object !== "object" || object.selection.type !== "pronoun") {
return vps;
return {
...vps,
blocks: adjustObjectSelection(
adjustSubjectSelection(vps.blocks, default2ndPersSubject),
{
type: "NP",
selection: {
...object.selection,
person: getNon2ndPersPronoun(),
},
}
return {
...vps,
blocks: adjustObjectSelection(vps.blocks, {
type: "NP",
selection: {
...object.selection,
person: getNon2ndPersPronoun(),
},
}),
};
}
if (!subjIs2ndPerson && objIs2ndPerson) {
if (typeof object !== "object" || object.selection.type !== "pronoun") {
return {
...vps,
blocks: adjustSubjectSelection(vps.blocks, default2ndPersSubject)
};
}
return {
...vps,
blocks: adjustObjectSelection(
adjustSubjectSelection(vps.blocks, default2ndPersSubject),
{
type: "NP",
selection: {
...object.selection,
person: getNon2ndPersPronoun(),
},
},
),
};
}
if (!subjIs2ndPerson && !objIs2ndPerson) {
return {
...vps,
blocks: adjustSubjectSelection(vps.blocks, default2ndPersSubject),
};
}
throw new Error("error ensuring compatible VPSelection for imperative verb");
),
};
}
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"
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
fp-ts@^2.16.0:
version "2.16.0"
resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-2.16.0.tgz#64e03314dfc1c7ce5e975d3496ac14bc3eb7f92e"
integrity sha512-bLq+KgbiXdTEoT1zcARrWEpa5z6A/8b7PcDW7Gef3NSisQ+VS7ll2Xbf1E+xsgik0rWub/8u0qP/iTTjj+PhxQ==
fragment-cache@^0.2.1:
version "0.2.1"
resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz"
@ -9539,6 +9544,11 @@ react-lifecycles-compat@^3.0.4:
resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
react-media-hook@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/react-media-hook/-/react-media-hook-0.5.0.tgz#f830231f31ea80049f8cbaf8058da90ab71e7150"
integrity sha512-OupDgOSCjUUWPiXq3HMoRwpsQry4cGf4vKzh2E984Xtm4I01ZFbq8JwCG/RPqXB9h0qxgzoYLbABC+LIZH8deQ==
react-overlays@^5.1.1:
version "5.1.1"
resolved "https://registry.npmjs.org/react-overlays/-/react-overlays-5.1.1.tgz"