more work, still need to get dynamic compounds working on new verb engine
This commit is contained in:
parent
68d83d95d4
commit
5f8c4ba876
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"typescript.preferences.autoImportFileExcludePatterns": [
|
||||
"../../library.ts"
|
||||
],
|
||||
}
|
|
@ -76,6 +76,8 @@
|
|||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"fp-ts": "^2.16.0",
|
||||
"react-media-hook": "^0.5.0",
|
||||
"react-select": "^5.4.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
export default PersInfsPicker;
|
||||
|
|
|
@ -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;
|
||||
export default TableCell;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
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}
|
||||
<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 : {}
|
||||
}
|
||||
>
|
||||
<RootsAndStems
|
||||
textOptions={textOptions}
|
||||
info={info}
|
||||
highlighted={highlightInRootsAndStems}
|
||||
hidePastParticiple={hidePastParticiple}
|
||||
<div style={title as any}>
|
||||
<div>Imperfective Stem</div>
|
||||
</div>
|
||||
<div style={indentR}>
|
||||
<VerbInfoItemDisplay
|
||||
item={pickPersInf(info.stem.imperfective, persInf)}
|
||||
textOptions={textOptions}
|
||||
tails={!noTails}
|
||||
/>
|
||||
</Hider>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={colClass}
|
||||
style={highlighted?.includes("perfective stem") ? highlight : {}}
|
||||
>
|
||||
<div style={title as any}>
|
||||
<div>Perfective Stem</div>
|
||||
</div>
|
||||
<div style={indentR}>
|
||||
<VerbInfoItemDisplay
|
||||
item={pickPersInf(perfectiveStem, persInf)}
|
||||
textOptions={textOptions}
|
||||
tails={!noTails}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={rowClass}>
|
||||
<div
|
||||
className={colClass}
|
||||
style={
|
||||
highlighted?.includes("imperfective root") ? highlight : {}
|
||||
}
|
||||
>
|
||||
<div style={title as any}>
|
||||
<div>Imperfective Root</div>
|
||||
</div>
|
||||
<div style={indentR}>
|
||||
<VerbInfoItemDisplay
|
||||
item={pickPersInf(info.root.imperfective, persInf)}
|
||||
textOptions={textOptions}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={colClass}
|
||||
style={highlighted?.includes("perfective root") ? highlight : {}}
|
||||
>
|
||||
<div>
|
||||
<div style={title as any}>
|
||||
<div>Perfective Root</div>
|
||||
</div>
|
||||
<div style={indentR}>
|
||||
<VerbInfoItemDisplay
|
||||
item={pickPersInf(perfectiveRoot, persInf)}
|
||||
textOptions={textOptions}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{!hidePastParticiple && "participle" in info && (
|
||||
<div
|
||||
className="text-center"
|
||||
style={highlighted?.includes("past participle") ? highlight : {}}
|
||||
>
|
||||
<div style={title as any}>Past Participle</div>
|
||||
<VerbInfoItemDisplay
|
||||
item={pickPersInf(info.participle.past, persInf)}
|
||||
textOptions={textOptions}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default VerbInfo;
|
||||
function VerbInfo({
|
||||
info,
|
||||
textOptions,
|
||||
showingStemsAndRoots,
|
||||
toggleShowingSar,
|
||||
highlightInRootsAndStems,
|
||||
hidePastParticiple,
|
||||
hideTypeInfo,
|
||||
}: {
|
||||
info: T.NonComboVerbInfo;
|
||||
textOptions: T.TextOptions;
|
||||
showingStemsAndRoots: boolean;
|
||||
highlightInRootsAndStems?: T.RootsOrStemsToHighlight;
|
||||
toggleShowingSar: () => void;
|
||||
hidePastParticiple?: boolean;
|
||||
hideTypeInfo?: boolean;
|
||||
}) {
|
||||
const inf = noPersInfs(info.root.imperfective).long;
|
||||
return (
|
||||
<div className="my-3">
|
||||
{!hideTypeInfo && <VerbTypeInfo info={info} textOptions={textOptions} />}
|
||||
<Hider
|
||||
showing={showingStemsAndRoots}
|
||||
label={`🌳 Roots and Stems for ${inf.p}`}
|
||||
handleChange={toggleShowingSar}
|
||||
hLevel={4}
|
||||
>
|
||||
<RootsAndStems
|
||||
textOptions={textOptions}
|
||||
info={info}
|
||||
highlighted={highlightInRootsAndStems}
|
||||
hidePastParticiple={hidePastParticiple}
|
||||
/>
|
||||
</Hider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default VerbInfo;
|
||||
|
|
|
@ -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;
|
||||
export default AllTensesDisplay;
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
export default ChartDisplay;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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],
|
||||
};
|
|
@ -10,50 +10,51 @@ import * as T from "../../types";
|
|||
import { fmapSingleOrLengthOpts } from "./fmaps";
|
||||
|
||||
export const blank: T.PsString = {
|
||||
p: "_____",
|
||||
f: "_____",
|
||||
p: "_____",
|
||||
f: "_____",
|
||||
};
|
||||
export const kidsBlank: T.PsString = { p: "___", f: "___" };
|
||||
|
||||
/**
|
||||
* returns the main entry of a VerbEntry or just the entry of a DictionaryEntry
|
||||
*
|
||||
*
|
||||
* @param e FullEntry
|
||||
* @returns DictionaryEntry
|
||||
*/
|
||||
export function entryOfFull(e: T.FullEntry): T.DictionaryEntry {
|
||||
return "entry" in e ? e.entry : e;
|
||||
return "entry" in e ? e.entry : e;
|
||||
}
|
||||
|
||||
// just for type safety
|
||||
export function noPersInfs<S 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,87 +253,92 @@ export const isSecondPerson = (p: T.Person) => [2, 3, 8, 9].includes(p);
|
|||
export const isThirdPerson = (p: T.Person) => [4, 5, 10, 11].includes(p);
|
||||
|
||||
export function incrementPerson(p: T.Person): T.Person {
|
||||
return (p + 1) % 12;
|
||||
return (p + 1) % 12;
|
||||
}
|
||||
|
||||
export function isSentenceForm(f: any): boolean {
|
||||
if ("long" in f) {
|
||||
return isSentenceForm(f.long);
|
||||
}
|
||||
return Array.isArray(f) && "p" in f[0];
|
||||
if ("long" in f) {
|
||||
return isSentenceForm(f.long);
|
||||
}
|
||||
return Array.isArray(f) && "p" in f[0];
|
||||
}
|
||||
|
||||
export function isNounAdjOrVerb(entry: T.DictionaryEntry): "nounAdj" | "verb" | false {
|
||||
if (!entry.c) {
|
||||
return false;
|
||||
}
|
||||
if (entry.c.includes("adj.") || entry.c.includes("n. m.") || entry.c.includes("n. f.")) {
|
||||
return "nounAdj";
|
||||
}
|
||||
if (entry.c.slice(0, 3) === "v. ") {
|
||||
return "verb";
|
||||
}
|
||||
export function isNounAdjOrVerb(
|
||||
entry: T.DictionaryEntry
|
||||
): "nounAdj" | "verb" | false {
|
||||
if (!entry.c) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
entry.c.includes("adj.") ||
|
||||
entry.c.includes("n. m.") ||
|
||||
entry.c.includes("n. f.")
|
||||
) {
|
||||
return "nounAdj";
|
||||
}
|
||||
if (entry.c.slice(0, 3) === "v. ") {
|
||||
return "verb";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* takes the ec field from a dictionary entry and produces an array of an EnglishVerbConjugation
|
||||
* for use with the conjugations display for showing English translation sentences of various verb
|
||||
* forms and conjugations
|
||||
*
|
||||
* @param ec
|
||||
* @returns
|
||||
*
|
||||
* @param ec
|
||||
* @returns
|
||||
*/
|
||||
export function parseEc(ec: string): T.EnglishVerbConjugationEc {
|
||||
function isVowel(s: string): boolean {
|
||||
return ["a", "e", "i", "o", "u"].includes(s);
|
||||
function isVowel(s: string): boolean {
|
||||
return ["a", "e", "i", "o", "u"].includes(s);
|
||||
}
|
||||
function makeRegularConjugations(s: string): T.EnglishVerbConjugationEc {
|
||||
if (s === "get") {
|
||||
return ["get", "gets", "getting", "got", "gotten"];
|
||||
}
|
||||
function makeRegularConjugations(s: string): T.EnglishVerbConjugationEc {
|
||||
if (s === "get") {
|
||||
return ["get","gets","getting","got","gotten"];
|
||||
}
|
||||
if (s === "become") {
|
||||
return ["become","becomes","becoming","became","become"];
|
||||
}
|
||||
if (s === "make") {
|
||||
return ["make","makes","making","made","made"];
|
||||
}
|
||||
if (s === "have") {
|
||||
return ["have","has","having","had","had"];
|
||||
}
|
||||
if (s === "be") {
|
||||
return ["am","is","being","was","been"];
|
||||
}
|
||||
if ((s.slice(-1) === "y") && !isVowel(s.slice(-2)[0])) {
|
||||
const b = s.slice(0, -1);
|
||||
return [`${s}`, `${b}ies`, `${s}ing`, `${b}ied`, `${b}ied`];
|
||||
}
|
||||
if (s.slice(-2) === "ss") {
|
||||
return [`${s}`, `${s}es`, `${s}ing`, `${s}ed`, `${s}ed`];
|
||||
}
|
||||
if (s.slice(-2) === "ie" && !isVowel(s.slice(-3)[0])) {
|
||||
const b = s.slice(0, -2);
|
||||
return [`${s}`, `${s}s`, `${b}ying`, `${s}d`, `${s}d`];
|
||||
}
|
||||
const b = s === ""
|
||||
? "VERB"
|
||||
: (s.slice(-1) === "e")
|
||||
? s.slice(0, -1)
|
||||
: s;
|
||||
return [`${s}`, `${s}s`, `${b}ing`, `${b}ed`, `${b}ed`];
|
||||
if (s === "become") {
|
||||
return ["become", "becomes", "becoming", "became", "become"];
|
||||
}
|
||||
const items = ec.split(",").map(x => x.trim());
|
||||
return (items.length === 4)
|
||||
? [items[0], items[1], items[2], items[3], items[3]]
|
||||
: (items.length === 5)
|
||||
? [items[0], items[1], items[2], items[3], items[4]]
|
||||
: makeRegularConjugations(items[0]);
|
||||
if (s === "make") {
|
||||
return ["make", "makes", "making", "made", "made"];
|
||||
}
|
||||
if (s === "have") {
|
||||
return ["have", "has", "having", "had", "had"];
|
||||
}
|
||||
if (s === "be") {
|
||||
return ["am", "is", "being", "was", "been"];
|
||||
}
|
||||
if (s.slice(-1) === "y" && !isVowel(s.slice(-2)[0])) {
|
||||
const b = s.slice(0, -1);
|
||||
return [`${s}`, `${b}ies`, `${s}ing`, `${b}ied`, `${b}ied`];
|
||||
}
|
||||
if (s.slice(-2) === "ss") {
|
||||
return [`${s}`, `${s}es`, `${s}ing`, `${s}ed`, `${s}ed`];
|
||||
}
|
||||
if (s.slice(-2) === "ie" && !isVowel(s.slice(-3)[0])) {
|
||||
const b = s.slice(0, -2);
|
||||
return [`${s}`, `${s}s`, `${b}ying`, `${s}d`, `${s}d`];
|
||||
}
|
||||
const b = s === "" ? "VERB" : s.slice(-1) === "e" ? s.slice(0, -1) : s;
|
||||
return [`${s}`, `${s}s`, `${b}ing`, `${b}ed`, `${b}ed`];
|
||||
}
|
||||
const items = ec.split(",").map((x) => x.trim());
|
||||
return items.length === 4
|
||||
? [items[0], items[1], items[2], items[3], items[3]]
|
||||
: items.length === 5
|
||||
? [items[0], items[1], items[2], items[3], items[4]]
|
||||
: makeRegularConjugations(items[0]);
|
||||
}
|
||||
|
||||
export function chooseLength<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
|
@ -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
|
@ -6,360 +6,471 @@
|
|||
*
|
||||
*/
|
||||
|
||||
import {
|
||||
concatPsString, trimOffPs,
|
||||
} from "../p-text-helpers";
|
||||
import { concatPsString, trimOffPs } from "../p-text-helpers";
|
||||
import * as T from "../../../types";
|
||||
import { makePsString, removeFVarientsFromVerb } from "../accent-and-ps-utils";
|
||||
import { accentOnNFromEnd, countSyllables, removeAccents } from "../accent-helpers";
|
||||
import {
|
||||
accentOnNFromEnd,
|
||||
countSyllables,
|
||||
removeAccents,
|
||||
} from "../accent-helpers";
|
||||
import { isKawulVerb, isTlulVerb } from "../type-predicates";
|
||||
import { vEntry, addAbilityEnding, weld, removeL, addTrailingAccent, tlulPerfectiveStem, getLongVB, possiblePPartLengths, isStatComp, statCompImperfectiveSpace, makeComplement, vTransitivity, isKedul } from "./rs-helpers";
|
||||
import {
|
||||
vEntry,
|
||||
addAbilityEnding,
|
||||
weld,
|
||||
removeL,
|
||||
addTrailingAccent,
|
||||
tlulPerfectiveStem,
|
||||
getLongVB,
|
||||
possiblePPartLengths,
|
||||
isStatComp,
|
||||
statCompImperfectiveSpace,
|
||||
makeComplement,
|
||||
vTransitivity,
|
||||
isKedul,
|
||||
} from "./rs-helpers";
|
||||
import { inflectPattern3 } from "./new-inflectors";
|
||||
import { fmapSingleOrLengthOpts } from "../fmaps";
|
||||
|
||||
const statVerb = {
|
||||
intransitive: vEntry({"ts":1581086654898,"i":11100,"p":"کېدل","f":"kedul","g":"kedul","e":"to become _____","r":2,"c":"v. intrans.","ssp":"ش","ssf":"sh","prp":"شول","prf":"shwul","pprtp":"شوی","pprtf":"shúwey","noOo":true,"ec":"become"}),
|
||||
transitive: vEntry({"ts":1579015359582,"i":11030,"p":"کول","f":"kawul","g":"kawul","e":"to make ____ ____ (as in \"He's making me angry.\")","r":4,"c":"v. trans.","ssp":"کړ","ssf":"kR","prp":"کړل","prf":"kRul","pprtp":"کړی","pprtf":"kúRey","noOo":true,"ec":"make,makes,making,made,made"}),
|
||||
intransitive: vEntry({
|
||||
ts: 1581086654898,
|
||||
i: 11100,
|
||||
p: "کېدل",
|
||||
f: "kedul",
|
||||
g: "kedul",
|
||||
e: "to become _____",
|
||||
r: 2,
|
||||
c: "v. intrans.",
|
||||
ssp: "ش",
|
||||
ssf: "sh",
|
||||
prp: "شول",
|
||||
prf: "shwul",
|
||||
pprtp: "شوی",
|
||||
pprtf: "shúwey",
|
||||
noOo: true,
|
||||
ec: "become",
|
||||
}),
|
||||
transitive: vEntry({
|
||||
ts: 1579015359582,
|
||||
i: 11030,
|
||||
p: "کول",
|
||||
f: "kawul",
|
||||
g: "kawul",
|
||||
e: 'to make ____ ____ (as in "He\'s making me angry.")',
|
||||
r: 4,
|
||||
c: "v. trans.",
|
||||
ssp: "کړ",
|
||||
ssf: "kR",
|
||||
prp: "کړل",
|
||||
prf: "kRul",
|
||||
pprtp: "کړی",
|
||||
pprtf: "kúRey",
|
||||
noOo: true,
|
||||
ec: "make,makes,making,made,made",
|
||||
}),
|
||||
};
|
||||
|
||||
const shwulVB: T.VBBasic = {
|
||||
type: "VB",
|
||||
ps: {
|
||||
long: [{ p: "شول", f: "shwul" }],
|
||||
short: [{ p: "شو", f: "shw" }],
|
||||
},
|
||||
}
|
||||
type: "VB",
|
||||
ps: {
|
||||
long: [{ p: "شول", f: "shwul" }],
|
||||
short: [{ p: "شو", f: "shw" }],
|
||||
},
|
||||
};
|
||||
const shVB: T.VBBasic = {
|
||||
type: "VB",
|
||||
ps: [{ p: "ش", f: "sh" }],
|
||||
}
|
||||
type: "VB",
|
||||
ps: [{ p: "ش", f: "sh" }],
|
||||
};
|
||||
|
||||
// TODO: figure out how to handle dynamic / stative verbs
|
||||
export function getRootStem({ verb, rs, aspect, type, genderNumber, voice }: {
|
||||
verb: T.VerbEntry,
|
||||
rs: "root" | "stem",
|
||||
aspect: T.Aspect,
|
||||
voice: T.Voice,
|
||||
type: "basic" | "ability",
|
||||
genderNumber: {
|
||||
gender: T.Gender,
|
||||
number: T.NounNumber,
|
||||
},
|
||||
export function getRootStem({
|
||||
verb,
|
||||
rs,
|
||||
aspect,
|
||||
type,
|
||||
genderNumber,
|
||||
voice,
|
||||
}: {
|
||||
verb: T.VerbEntry;
|
||||
rs: "root" | "stem";
|
||||
aspect: T.Aspect;
|
||||
voice: T.Voice;
|
||||
type: "basic" | "ability";
|
||||
genderNumber: {
|
||||
gender: T.Gender;
|
||||
number: T.NounNumber;
|
||||
};
|
||||
}): T.RootsStemsOutput {
|
||||
const v = removeFVarientsFromVerb(verb);
|
||||
if (type === "ability") {
|
||||
return getAbilityRs(v, aspect, rs, voice, genderNumber);
|
||||
}
|
||||
if (voice === "passive") {
|
||||
return getPassiveRs(v, aspect, rs, genderNumber);
|
||||
}
|
||||
return rs === "stem"
|
||||
? getStem(v, genderNumber, aspect)
|
||||
: getRoot(v, genderNumber, aspect);
|
||||
const v = removeFVarientsFromVerb(verb);
|
||||
if (type === "ability") {
|
||||
return getAbilityRs(v, aspect, rs, voice, genderNumber);
|
||||
}
|
||||
if (voice === "passive") {
|
||||
return getPassiveRs(v, aspect, rs, genderNumber);
|
||||
}
|
||||
return rs === "stem"
|
||||
? getStem(v, genderNumber, aspect)
|
||||
: getRoot(v, genderNumber, aspect);
|
||||
}
|
||||
|
||||
function getAbilityRs(
|
||||
verb: T.VerbEntryNoFVars,
|
||||
aspect: T.Aspect,
|
||||
rs: "root" | "stem",
|
||||
voice: T.Voice,
|
||||
genderNum: T.GenderNumber,
|
||||
verb: T.VerbEntryNoFVars,
|
||||
aspect: T.Aspect,
|
||||
rs: "root" | "stem",
|
||||
voice: T.Voice,
|
||||
genderNum: T.GenderNumber
|
||||
): [[] | [T.VHead], [T.VB, T.VBA]] {
|
||||
const losesAspect = isTlulVerb(verb) || (isStatComp(verb) && vTransitivity(verb) === "intransitive");
|
||||
const [vhead, [basicroot]] = voice === "passive"
|
||||
? getPassiveRs(verb, "imperfective", "root", genderNum)
|
||||
: getRoot(verb, genderNum, losesAspect ? "imperfective" : aspect);
|
||||
return [
|
||||
vhead,
|
||||
[
|
||||
addAbilityEnding(basicroot),
|
||||
rs === "root" ? shwulVB : shVB,
|
||||
],
|
||||
];
|
||||
const losesAspect =
|
||||
isTlulVerb(verb) ||
|
||||
(isStatComp(verb) && vTransitivity(verb) === "intransitive");
|
||||
const [vhead, [basicroot]] =
|
||||
voice === "passive"
|
||||
? getPassiveRs(verb, "imperfective", "root", genderNum)
|
||||
: getRoot(verb, genderNum, losesAspect ? "imperfective" : aspect);
|
||||
return [vhead, [addAbilityEnding(basicroot), rs === "root" ? shwulVB : shVB]];
|
||||
}
|
||||
|
||||
export function getPastParticiple(verb: T.VerbEntry, voice: T.Voice, { gender, number }: { gender: T.Gender, number: T.NounNumber }): T.VBGenNum | T.WeldedGN {
|
||||
const v = removeFVarientsFromVerb(verb);
|
||||
if (voice === "passive") {
|
||||
return getPassivePp(v, { gender, number });
|
||||
}
|
||||
if (isStatComp(v) && v.complement) {
|
||||
return weld(
|
||||
makeComplement(v.complement, { gender, number }),
|
||||
getPastParticiple(
|
||||
statVerb[vTransitivity(verb)],
|
||||
voice,
|
||||
{ gender, number },
|
||||
) as T.VBGenNum,
|
||||
);
|
||||
}
|
||||
if (verb.entry.pprtp && verb.entry.pprtf) {
|
||||
const base = makePsString(verb.entry.pprtp, verb.entry.pprtf);
|
||||
return {
|
||||
type: "VB",
|
||||
ps: inflectPattern3(base, { gender, number }),
|
||||
gender,
|
||||
number,
|
||||
};
|
||||
}
|
||||
const basicRoot = getRoot(removeFVarientsFromVerb(verb), { gender, number }, "imperfective")[1][0];
|
||||
const longRoot = getLongVB(basicRoot);
|
||||
const rootWLengths = possiblePPartLengths(longRoot);
|
||||
/* istanbul ignore next */
|
||||
if ("right" in rootWLengths) {
|
||||
throw new Error("should not have welded here");
|
||||
}
|
||||
return {
|
||||
...rootWLengths,
|
||||
ps: addTail(rootWLengths.ps),
|
||||
export function getPastParticiple(
|
||||
verb: T.VerbEntry,
|
||||
voice: T.Voice,
|
||||
{ gender, number }: { gender: T.Gender; number: T.NounNumber }
|
||||
): T.VBGenNum | T.WeldedGN {
|
||||
const v = removeFVarientsFromVerb(verb);
|
||||
if (voice === "passive") {
|
||||
return getPassivePp(v, { gender, number });
|
||||
}
|
||||
if (isStatComp(v) && v.complement) {
|
||||
return weld(
|
||||
makeComplement(v.complement, { gender, number }),
|
||||
getPastParticiple(statVerb[vTransitivity(verb)], voice, {
|
||||
gender,
|
||||
number,
|
||||
}) as T.VBGenNum
|
||||
);
|
||||
}
|
||||
if (verb.entry.pprtp && verb.entry.pprtf) {
|
||||
const base = makePsString(verb.entry.pprtp, verb.entry.pprtf);
|
||||
return {
|
||||
type: "VB",
|
||||
ps: inflectPattern3(base, { gender, number }),
|
||||
gender,
|
||||
number,
|
||||
};
|
||||
|
||||
function addTail(ps: T.SingleOrLengthOpts<T.PsString[]>): T.SingleOrLengthOpts<T.PsString[]> {
|
||||
return fmapSingleOrLengthOpts((x) => {
|
||||
const withTail = concatPsString(x[0], { p: "ی", f: "ey"});
|
||||
return inflectPattern3(withTail, { gender, number });
|
||||
}, ps);
|
||||
}
|
||||
}
|
||||
const basicRoot = getRoot(
|
||||
removeFVarientsFromVerb(verb),
|
||||
{ gender, number },
|
||||
"imperfective"
|
||||
)[1][0];
|
||||
const longRoot = getLongVB(basicRoot);
|
||||
const rootWLengths = possiblePPartLengths(longRoot);
|
||||
/* istanbul ignore next */
|
||||
if ("right" in rootWLengths) {
|
||||
throw new Error("should not have welded here");
|
||||
}
|
||||
return {
|
||||
...rootWLengths,
|
||||
ps: addTail(rootWLengths.ps),
|
||||
gender,
|
||||
number,
|
||||
};
|
||||
|
||||
function addTail(
|
||||
ps: T.SingleOrLengthOpts<T.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" }],
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
1601
src/types.ts
1601
src/types.ts
File diff suppressed because it is too large
Load Diff
10
yarn.lock
10
yarn.lock
|
@ -5425,6 +5425,11 @@ forwarded@0.2.0:
|
|||
resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz"
|
||||
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
|
||||
|
||||
fp-ts@^2.16.0:
|
||||
version "2.16.0"
|
||||
resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-2.16.0.tgz#64e03314dfc1c7ce5e975d3496ac14bc3eb7f92e"
|
||||
integrity sha512-bLq+KgbiXdTEoT1zcARrWEpa5z6A/8b7PcDW7Gef3NSisQ+VS7ll2Xbf1E+xsgik0rWub/8u0qP/iTTjj+PhxQ==
|
||||
|
||||
fragment-cache@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz"
|
||||
|
@ -9539,6 +9544,11 @@ react-lifecycles-compat@^3.0.4:
|
|||
resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz"
|
||||
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
|
||||
|
||||
react-media-hook@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/react-media-hook/-/react-media-hook-0.5.0.tgz#f830231f31ea80049f8cbaf8058da90ab71e7150"
|
||||
integrity sha512-OupDgOSCjUUWPiXq3HMoRwpsQry4cGf4vKzh2E984Xtm4I01ZFbq8JwCG/RPqXB9h0qxgzoYLbABC+LIZH8deQ==
|
||||
|
||||
react-overlays@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.npmjs.org/react-overlays/-/react-overlays-5.1.1.tgz"
|
||||
|
|
Loading…
Reference in New Issue