pretty much working with new verb engine and verb explorer ... quiz doesn't seem to be working right now

This commit is contained in:
adueck 2023-07-22 18:13:52 +04:00
parent 5f8c4ba876
commit 3b6d013402
32 changed files with 39977 additions and 3698 deletions

11
package-lock.json generated
View File

@ -10,6 +10,7 @@
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"fp-ts": "^2.16.0",
"react-select": "^5.4.0"
},
"devDependencies": {
@ -10735,6 +10736,11 @@
"node": ">= 0.6"
}
},
"node_modules/fp-ts": {
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.0.tgz",
"integrity": "sha512-bLq+KgbiXdTEoT1zcARrWEpa5z6A/8b7PcDW7Gef3NSisQ+VS7ll2Xbf1E+xsgik0rWub/8u0qP/iTTjj+PhxQ=="
},
"node_modules/fragment-cache": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
@ -31006,6 +31012,11 @@
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"dev": true
},
"fp-ts": {
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.0.tgz",
"integrity": "sha512-bLq+KgbiXdTEoT1zcARrWEpa5z6A/8b7PcDW7Gef3NSisQ+VS7ll2Xbf1E+xsgik0rWub/8u0qP/iTTjj+PhxQ=="
},
"fragment-cache": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",

View File

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

View File

@ -9,9 +9,7 @@
import { useEffect, useState } from "react";
import ButtonSelect from "./components/src/ButtonSelect";
import {
Modal
} from "react-bootstrap";
import { Modal } from "react-bootstrap";
import * as T from "./types";
import defualtTextOptions from "./lib/src/default-text-options";
import useStickyState from "./components/src/useStickyState";
@ -23,28 +21,43 @@ import InflectionDemo from "./demo-components/InflectionDemo";
import SpellingDemo from "./demo-components/SpellingDemo";
function App() {
const [showingTextOptions, setShowingTextOptions] = useStickyState<boolean>(false, "showTextOpts1");
const [textOptions, setTextOptions] = useStickyState<T.TextOptions>(defualtTextOptions, "textOpts1");
const [showingTextOptions, setShowingTextOptions] = useStickyState<boolean>(
false,
"showTextOpts1"
);
const [textOptions, setTextOptions] = useStickyState<T.TextOptions>(
defualtTextOptions,
"textOpts1"
);
const [theme, setTheme] = useStickyState<"light" | "dark">("light", "theme1");
const [showing, setShowing] = useState<string>("");
function handleHiderClick(label: string) {
setShowing(os => os === label
? ""
: label);
setShowing((os) => (os === label ? "" : label));
}
useEffect(() => {
document.documentElement.setAttribute("data-theme", theme);
}, [theme]);
return <>
return (
<>
<main className="flex-shrink-0 mb-4">
<div className="container" style={{ maxWidth: "800px" }}>
<div style={{ position: "absolute", top: "1.5rem", right: "1.5rem", display: "flex", flexDirection: "row" }}>
<div
style={{
position: "absolute",
top: "1.5rem",
right: "1.5rem",
display: "flex",
flexDirection: "row",
}}
>
<div
className="clickable mr-3"
onClick={() => setTheme(theme === "light" ? "dark" : "light")}
>
<i className={`fa-lg fas fa-${theme === "light" ? "sun" : "moon"}`} />
<i
className={`fa-lg fas fa-${theme === "light" ? "sun" : "moon"}`}
/>
</div>
<div
className="clickable"
@ -53,14 +66,36 @@ function App() {
<i className="fa-lg fas fa-cog" />
</div>
</div>
<div className="text-center mb-4" style={{ marginTop: "3rem", marginBottom: "1rem" }}>
<h1 className="display-4 mt-2"><code>Pashto Inflector</code></h1>
<p className="lead my-3" style={{ maxWidth: "600px", margin: "0 auto" }}>
An open source TypeScript/React library for Pashto inflection, verb conjugation, phrase generation, text conversion, and more
<div
className="text-center mb-4"
style={{ marginTop: "3rem", marginBottom: "1rem" }}
>
<h1 className="display-4 mt-2">
<code>Pashto Inflector</code>
</h1>
<p
className="lead my-3"
style={{ maxWidth: "600px", margin: "0 auto" }}
>
An open source TypeScript/React library for Pashto inflection,
verb conjugation, phrase generation, text conversion, and more
</p>
<p>
Used in the{" "}
<a href="https://dictionary.lingdocs.com">
LingDocs Pashto Dictionary
</a>{" "}
and{" "}
<a href="https://grammar.lingdocs.com">LingDocs Pashto Grammar</a>
</p>
<p>
by <a href="https://adueck.github.io">Adam Dueck</a> - GPLv3
licensed{" "}
<a href="https://github.com/lingdocs/pashto-inflector">
Source Code
</a>{" "}
on GitHub
</p>
<p>Used in the <a href="https://dictionary.lingdocs.com">LingDocs Pashto Dictionary</a> and <a href="https://grammar.lingdocs.com">LingDocs Pashto Grammar</a></p>
<p>by <a href="https://adueck.github.io">Adam Dueck</a> - GPLv3 licensed <a href="https://github.com/lingdocs/pashto-inflector">Source Code</a> on GitHub</p>
</div>
<h2 className="mb-3">Demos:</h2>
<Hider
@ -78,10 +113,7 @@ function App() {
handleChange={() => handleHiderClick("equatives")}
>
<div className="mt-4" style={{ paddingBottom: "20px" }}>
<EPExplorer
opts={textOptions}
entryFeeder={entryFeeder}
/>
<EPExplorer opts={textOptions} entryFeeder={entryFeeder} />
</div>
</Hider>
<Hider
@ -102,7 +134,10 @@ function App() {
</Hider>
</div>
</main>
<Modal show={showingTextOptions} onHide={() => setShowingTextOptions(false)}>
<Modal
show={showingTextOptions}
onHide={() => setShowingTextOptions(false)}
>
<Modal.Header closeButton>
<Modal.Title>Settings</Modal.Title>
</Modal.Header>
@ -129,7 +164,9 @@ function App() {
{ label: "Off", value: "false" },
]}
value={textOptions.diacritics.toString()}
handleChange={(p) => setTextOptions({ ...textOptions, diacritics: p === "true" })}
handleChange={(p) =>
setTextOptions({ ...textOptions, diacritics: p === "true" })
}
/>
<h6 className="mt-3">Pashto Text Size</h6>
<ButtonSelect
@ -139,7 +176,9 @@ function App() {
{ label: "X-Large", value: "largest" },
]}
value={textOptions.pTextSize}
handleChange={(p) => setTextOptions({ ...textOptions, pTextSize: p })}
handleChange={(p) =>
setTextOptions({ ...textOptions, pTextSize: p })
}
/>
<h6 className="mt-3">Phonetics</h6>
<ButtonSelect
@ -150,11 +189,17 @@ function App() {
// { label: "None", value: "none" },
]}
value={textOptions.phonetics}
handleChange={(p) => setTextOptions({ ...textOptions, phonetics: p })}
handleChange={(p) =>
setTextOptions({ ...textOptions, phonetics: p })
}
/>
</Modal.Body>
<Modal.Footer>
<button type="button" className="btn btn-primary clb" onClick={() => setShowingTextOptions(false)}>
<button
type="button"
className="btn btn-primary clb"
onClick={() => setShowingTextOptions(false)}
>
Close
</button>
</Modal.Footer>
@ -165,6 +210,7 @@ function App() {
</div>
</footer> */}
</>
);
}
export default App;

35162
src/components/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,4 @@
import {
makeNounSelection,
} from "../../../lib/src/phrase-building/make-selections";
import { makeNounSelection } from "../../../lib/src/phrase-building/make-selections";
import * as T from "../../../types";
import ButtonSelect from "../ButtonSelect";
@ -57,12 +55,13 @@ import AdjectiveManager from "./AdjectiveManager";
// }
function NPNounPicker(props: {
entryFeeder: T.EntryFeeder,
noun: T.NounSelection | undefined,
onChange: (p: T.NounSelection | undefined) => void,
opts: T.TextOptions,
phraseIsComplete: boolean,
entryFeeder: T.EntryFeeder;
noun: T.NounSelection | undefined;
onChange: (p: T.NounSelection | undefined) => void;
opts: T.TextOptions;
phraseIsComplete: boolean;
}) {
console.log({ noun: props.noun });
// const [patternFilter, setPatternFilter] = useState<FilterPattern | undefined>(undefined);
// const [showFilter, setShowFilter] = useState<boolean>(false)
// const nounsFiltered = props.nouns
@ -86,7 +85,9 @@ function NPNounPicker(props: {
});
}
}
function handleDemonstrativeUpdate(demonstrative: undefined | T.NounSelection["demonstrative"]) {
function handleDemonstrativeUpdate(
demonstrative: undefined | T.NounSelection["demonstrative"]
) {
if (props.noun) {
props.onChange({
...props.noun,
@ -94,7 +95,8 @@ function NPNounPicker(props: {
});
}
}
return <div style={{ maxWidth: "225px", minWidth: "125px" }}>
return (
<div style={{ maxWidth: "225px", minWidth: "125px" }}>
{/* {showFilter && <div className="mb-2 text-center">
<div className="d-flex flex-row justify-content-between">
<div className="text-small mb-1">Filter by inflection pattern</div>
@ -108,7 +110,8 @@ function NPNounPicker(props: {
handleChange={setPatternFilter}
/>
</div>} */}
{props.noun && <AdjectiveManager
{props.noun && (
<AdjectiveManager
phraseIsComplete={props.phraseIsComplete}
adjectives={props.noun?.adjectives}
demonstrative={props.noun.demonstrative}
@ -116,9 +119,14 @@ function NPNounPicker(props: {
opts={props.opts}
onChange={handelAdjectivesUpdate}
onDemonstrativeChange={handleDemonstrativeUpdate}
/>}
/>
)}
<div className="h6">Noun</div>
{!(props.noun && props.noun.dynamicComplement) ? <div>
{!(
props.noun &&
(props.noun.dynamicComplement || props.noun.genStativeComplement)
) ? (
<div>
<EntrySelect
value={props.noun?.entry}
entryFeeder={props.entryFeeder.nouns}
@ -126,20 +134,31 @@ function NPNounPicker(props: {
name="Noun"
opts={props.opts}
/>
</div> : <div>
{props.noun && <div>
<div className="mb-2">Included in Dyn. Compound:</div>
</div>
) : (
<div>
{props.noun && (
<div>
<div className="mb-2">
Included in{" "}
{props.noun.genStativeComplement ? "Gen. Stat." : "Dyn."}{" "}
Compound:
</div>
<div className="mb-3 text-center">
<InlinePs opts={props.opts}>
{{ p: props.noun.entry.p, f: props.noun.entry.f }}
</InlinePs>
<div className="text-muted">{props.noun.entry.e}</div>
</div>
</div>}
</div>}
{props.noun && <div className="my-2 d-flex flex-row justify-content-around align-items-center">
</div>
)}
</div>
)}
{props.noun && (
<div className="my-2 d-flex flex-row justify-content-around align-items-center">
<div>
{props.noun.genderCanChange ? <ButtonSelect
{props.noun.genderCanChange ? (
<ButtonSelect
small
options={[
{ label: "Masc", value: "masc" },
@ -153,10 +172,16 @@ function NPNounPicker(props: {
gender,
});
}}
/> : props.noun.gender === "masc" ? "Masc." : "Fem."}
/>
) : props.noun.gender === "masc" ? (
"Masc."
) : (
"Fem."
)}
</div>
<div>
{props.noun.numberCanChange ? <ButtonSelect
{props.noun.numberCanChange ? (
<ButtonSelect
small
options={[
{ label: "Sing.", value: "singular" },
@ -170,10 +195,17 @@ function NPNounPicker(props: {
number,
});
}}
/> : props.noun.number === "singular" ? "Sing." : "Plur."}
/>
) : props.noun.number === "singular" ? (
"Sing."
) : (
"Plur."
)}
</div>
</div>}
</div>;
</div>
)}
</div>
);
}
export default NPNounPicker;

View File

@ -9,13 +9,21 @@ import Hider from "../Hider";
import * as T from "../../../types";
import useStickyState from "../useStickyState";
import { isImperativeTense } from "../../../lib/src/type-predicates";
import ButtonSelect from "../ButtonSelect";
import { VpsReducerAction } from "../../../lib/src/phrase-building/vps-reducer";
import { useState } from "react";
import { statVerb } from "../../../lib/src/new-verb-engine/roots-and-stems";
// import { conjugateVerb } from "../../../lib/src/verb-conjugation";
function AllTensesDisplay({
VS,
opts,
onChange,
object,
}: {
onChange: (p: VpsReducerAction) => void;
VS: T.VerbSelection;
object: T.NPSelection | T.ObjectNP | undefined;
opts: T.TextOptions;
}) {
const [showing, setShowing] = useStickyState<string[]>([], "VPTensesShowing");
@ -23,6 +31,11 @@ function AllTensesDisplay({
false,
"showFormulasWithCharts"
);
const [objectNPNumber, setObjectNPNumber] = useState<T.NounNumber>(
object && typeof object === "object" && object.selection.type === "noun"
? object.selection.number
: "singular"
);
const adjustShowing = (v: string) => {
if (showing.includes(v)) {
setShowing((os) => os.filter((x) => x !== v));
@ -56,14 +69,69 @@ function AllTensesDisplay({
? (`${baseTense}Modal` as T.AbilityTense)
: baseTense;
}
const objectNP: T.NPSelection | undefined =
object && typeof object === "object" && object.selection.type === "noun"
? object
: undefined;
const verb =
VS.isCompound === "generative stative"
? statVerb[
VS.transitivity === "intransitive" ? "intransitive" : "transitive"
]
: VS.dynAuxVerb
? VS.dynAuxVerb
: VS.verb;
return (
<div>
<div
className="clickable mb-2 small text-center"
onClick={() => setShowFormulas((x) => !x)}
className="small clickable text-right mb-2"
>
🧪 {!showFormulas ? "Show" : "Hide"} Formulas
</div>
<div className="mb-2 d-flex flex-row justify-content-around align-items-center">
{objectNP?.selection.type === "noun" &&
objectNP.selection.numberCanChange && (
<div>
<span className="text-muted small mr-2">comp. noun:</span>
<ButtonSelect
xSmall
value={objectNPNumber}
options={[
{
label: "sing.",
value: "singular",
},
{
label: "plur.",
value: "plural",
},
]}
handleChange={(number) => {
setObjectNPNumber(number);
}}
/>
</div>
)}
<ButtonSelect
xSmall
value={VS.negative.toString() as "true" | "false"}
options={[
{
label: "Pos.",
value: "false",
},
{
label: "Neg.",
value: "true",
},
]}
handleChange={(payload) =>
onChange({ type: "set negativity", payload })
}
/>
</div>
{options.map((tense) => {
const t = getTense(tense.value);
return (
@ -81,7 +149,20 @@ function AllTensesDisplay({
)}
{showing && (
<ChartDisplay
verb={VS.verb}
verb={verb}
objectNP={
objectNP?.selection.type === "noun"
? {
...objectNP,
selection: {
...objectNP.selection,
number: objectNP.selection.numberCanChange
? objectNPNumber
: objectNP.selection.number,
},
}
: undefined
}
negative={VS.negative}
tense={t}
transitivity={VS.transitivity}

View File

@ -8,7 +8,13 @@ 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";
import "./form-display.css";
import {
makeBlock,
makeKid,
} from "../../../lib/src/phrase-building/blocks-utils";
import InlinePs from "../InlinePs";
import { personToGenNum } from "../../../lib/src/misc-helpers";
export const roleIcon = {
king: <i className="mx-1 fas fa-crown" />,
@ -21,14 +27,21 @@ function VerbChartDisplay({
shortDefault,
transitivity,
past,
negative,
imperative,
}: {
chart: T.OptionalPersonInflections<
chart: {
objNP: T.Rendered<T.NPSelection> | undefined;
vbs: T.OptionalPersonInflections<
T.SingleOrLengthOpts<T.RenderVerbOutput[]>
>;
};
opts: T.TextOptions;
shortDefault?: boolean;
transitivity: T.Transitivity;
past: boolean;
negative: boolean;
imperative: boolean;
}) {
const [length, setLength] = useState<T.Length>(
shortDefault ? "short" : "long"
@ -37,13 +50,23 @@ function VerbChartDisplay({
useEffect(() => {
setLength("long");
}, [chart]);
const desktop = useMediaPredicate("(min-width: 600px)");
const chartWPers = choosePersInf(chart, persInf);
const chartWPers = choosePersInf(chart.vbs, persInf);
const chartWLength = getLength(chartWPers, length);
const x = chartWLength.map(renderVerbOutputToText(false));
const x = chartWLength.map(
renderVerbOutputToText({
objNP: chart.objNP,
negative,
hasBa: chartWLength[0].hasBa,
imperative,
intransitive: transitivity === "intransitive",
})
);
const verbBlock =
x.length === 12
x.length === 1
? // unchanging gramm trans or dynamic compound past transitive
[[[x[0]]]]
: x.length === 12
? [
// 1st pers
[
@ -83,10 +106,15 @@ function VerbChartDisplay({
</div>
<div className="row">
<div className="col">
<AgreementInfo transitivity={transitivity} past={past} />
<AgreementInfo
opts={opts}
transitivity={transitivity}
past={past}
objNP={chart.objNP}
/>
</div>
<div className="col">
{"long" in chartWPers && (
{"long" in chartWPers && lengthsMakeADiff(chartWPers) && (
<LengthSelection
hasMini={"mini" in chartWPers}
value={length}
@ -94,8 +122,13 @@ function VerbChartDisplay({
/>
)}
</div>
{desktop && <div className="col" />}
<div className="hide-on-mobile col" />
</div>
{verbBlock.length === 1 ? (
<div className="d-flex flex-row justify-content-center mt-3 text-center">
<TableCell item={verbBlock[0][0][0]} textOptions={opts} />
</div>
) : (
<table className="table mt-2" style={{ tableLayout: "fixed" }}>
<thead>
<tr>
@ -118,6 +151,7 @@ function VerbChartDisplay({
))}
</tbody>
</table>
)}
</>
);
}
@ -224,32 +258,93 @@ function LengthSelection({
);
}
function renderVerbOutputToText(negative: boolean) {
return function (v: T.RenderVerbOutput): T.PsString[] {
const blocks = insertNegative(
v.vbs,
function renderVerbOutputToText({
objNP,
negative,
false /* TODO: apply imperative */
hasBa,
imperative,
intransitive,
}: {
objNP: T.Rendered<T.NPSelection> | undefined;
negative: boolean;
hasBa: boolean;
imperative: boolean;
intransitive: boolean;
}) {
return function (v: T.RenderVerbOutput): T.PsString[] {
const blocks: T.Block[][] = insertNegative(v.vbs, negative, imperative).map(
(b) => {
if (!objNP) return b;
return [
...(objNP
? [makeBlock({ type: "objectSelection", selection: objNP })]
: []),
...b,
];
}
);
return combineIntoText(blocks, 0 /* TODO: why is this needed */);
const b = hasBa
? blocks.flatMap((v) => [
[
{ p: " ... ", f: " ... " },
...[makeKid({ type: "ba" }), { p: " ... ", f: " ... " }],
...v,
],
...(v.length > 1 && intransitive
? [[v[0], makeKid({ type: "ba" }), ...v.slice(1)]]
: []),
])
: blocks;
return combineIntoText(b, 0 /* TODO: why is this needed */);
};
}
function AgreementInfo({
transitivity,
past,
objNP,
opts,
}: {
transitivity: T.Transitivity;
past: boolean;
objNP: T.Rendered<T.NPSelection> | undefined;
opts: T.TextOptions;
}) {
function printGenNum({ gender, number }: T.GenderNumber): string {
return `${gender === "masc" ? "m." : "f."} ${
number === "singular" ? "s." : "pl."
}`;
}
return (
<div className="text-muted small mt-1">
<div>
{roleIcon.king} agrees w/{" "}
<strong>
{transitivity !== "intransitive" && past ? "object" : "subject"}
</strong>
{transitivity === "transitive" && past && objNP ? " noun" : ""}
</div>
{transitivity === "transitive" && past && objNP && (
<div>
<InlinePs opts={opts}>{objNP.selection.ps[0]}</InlinePs>
{` `}({printGenNum(personToGenNum(objNP.selection.person))})
</div>
)}
{transitivity === "grammatically transitive" && past && (
<div>(implied 3rd pers. m. pl.)</div>
)}
</div>
);
}
function lengthsMakeADiff(v: T.LengthOptions<T.RenderVerbOutput[]>): boolean {
if (v.long.length > 1) {
return true;
}
const longSample = combineIntoText([[makeBlock(v.long[0].vbs[1][0])]], 0);
const shortSample = combineIntoText([[makeBlock(v.short[0].vbs[1][0])]], 0);
return !eqPsStringWVars.equals(longSample, shortSample);
}
export default VerbChartDisplay;

View File

@ -3,6 +3,8 @@ import * as T from "../../../types";
import { buildVerbChart } from "./chart-builder";
import { isPastTense } from "../../library";
// TODO - combine this with NewVerbFormDisplay
function ChartDisplay({
verb,
tense,
@ -11,6 +13,7 @@ function ChartDisplay({
imperative,
negative,
transitivity,
objectNP,
}: {
verb: T.VerbEntry;
tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense;
@ -19,6 +22,7 @@ function ChartDisplay({
imperative: boolean;
negative: boolean;
transitivity: T.Transitivity;
objectNP: T.NPSelection | undefined;
}) {
const verbChart = buildVerbChart({
verb,
@ -27,10 +31,13 @@ function ChartDisplay({
negative,
transitivity,
imperative,
objectNP,
});
return (
<div className="mb-4">
<NewVerbFormDisplay
imperative={imperative}
negative={negative}
chart={verbChart}
opts={opts}
transitivity={transitivity}

View File

@ -28,28 +28,28 @@ export const vpPhraseURLParam = "vp";
// TODO: error handling on error with rendering etc
function VPExplorer(props: {
loaded?: T.VPSelectionState,
verb: T.VerbEntry,
opts: T.TextOptions,
handleLinkClick: ((ts: number) => void) | "none",
entryFeeder: T.EntryFeeder,
onlyPhrases?: boolean,
loaded?: T.VPSelectionState;
verb: T.VerbEntry;
opts: T.TextOptions;
handleLinkClick: ((ts: number) => void) | "none";
entryFeeder: T.EntryFeeder;
onlyPhrases?: boolean;
}) {
const [vps, adjustVps] = useStickyReducer(
vpsReducer,
props.loaded
? props.loaded
: savedVps => makeVPSelectionState(props.verb, savedVps),
: (savedVps) => makeVPSelectionState(props.verb, savedVps),
"vpsState16",
flashMessage,
flashMessage
);
const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">(
savedMode => {
(savedMode) => {
if (!savedMode) return props.onlyPhrases ? "phrases" : "charts";
if (savedMode === "quiz") return "phrases";
return savedMode;
},
"verbExplorerMode2",
"verbExplorerMode2"
);
const [showClipped, setShowClipped] = useState<string>("");
const [
@ -87,7 +87,7 @@ function VPExplorer(props: {
setMode("phrases");
adjustVps({
type: "load vps",
payload: VPSFromUrl
payload: VPSFromUrl,
});
}
// eslint-disable-next-line
@ -130,16 +130,17 @@ function VPExplorer(props: {
const object = getObjectSelection(vps.blocks).selection;
const VPS = completeVPSelection(vps);
const phraseIsComplete = !!VPS;
return <div className="mt-3" style={{ maxWidth: "950px"}}>
return (
<div className="mt-3" style={{ maxWidth: "950px" }}>
<VerbPicker
vps={vps}
onChange={quizLock(adjustVps)}
opts={props.opts}
handleLinkClick={props.handleLinkClick}
/>
{!props.onlyPhrases && <div className="mt-2 mb-3 d-flex flex-row justify-content-between align-items-center">
<div style={{ width: "1rem" }}>
</div>
{!props.onlyPhrases && (
<div className="mt-2 mb-3 d-flex flex-row justify-content-between align-items-center">
<div style={{ width: "1rem" }}></div>
<ButtonSelect
value={mode}
options={[
@ -155,7 +156,11 @@ function VPExplorer(props: {
onClick={mode === "phrases" ? handleCopyCode : undefined}
style={{ width: "1rem" }}
>
{(mode === "phrases" && phraseIsComplete) ? <i className="fas fa-code" /> : ""}
{mode === "phrases" && phraseIsComplete ? (
<i className="fas fa-code" />
) : (
""
)}
</div>
<div
className="clickable"
@ -165,52 +170,79 @@ function VPExplorer(props: {
{mode === "phrases" ? <i className="fas fa-share-alt" /> : ""}
</div>
</div>
</div>}
{(vps.verb && (typeof object === "object") && (vps.verb.isCompound !== "dynamic") && (vps.verb.tenseCategory !== "imperative") &&(mode === "phrases")) &&
</div>
)}
{vps.verb &&
typeof object === "object" &&
vps.verb.isCompound !== "dynamic" &&
vps.verb.tenseCategory !== "imperative" &&
mode === "phrases" && (
<div className="text-center my-2">
<button onClick={handleSubjObjSwap} className="btn btn-sm btn-light">
<button
onClick={handleSubjObjSwap}
className="btn btn-sm btn-light"
>
<i className="fas fa-exchange-alt mr-2" /> subj/obj
</button>
</div>}
{mode === "phrases" && <VPPicker
</div>
)}
{mode === "phrases" && (
<VPPicker
opts={props.opts}
entryFeeder={props.entryFeeder}
onChange={(payload) => adjustVps({ type: "load vps", payload })}
vps={vps}
/>}
{mode !== "phrases" && <div className="my-2">
<TensePicker
vps={vps}
onChange={adjustVps}
mode={mode}
/>
</div>}
{mode === "phrases" && <VPDisplay
VPS={vps}
opts={props.opts}
setForm={handleSetForm}
/>}
{mode === "charts" && <AllTensesDisplay VS={vps.verb} opts={props.opts} />}
{mode === "quiz" && <VPExplorerQuiz opts={props.opts} vps={vps} />}
{showClipped && <div className="alert alert-primary text-center" role="alert" style={{
position: "fixed",
top: "30%",
left: "50%",
transform: "translate(-50%, -50%)",
zIndex: 9999999999999,
}}>
{showClipped}
</div>}
{alertMsg && <div className="alert alert-warning text-center" role="alert" style={{
position: "fixed",
top: "30%",
left: "50%",
transform: "translate(-50%, -50%)",
zIndex: 9999999999999,
}}>
{alertMsg}
</div>}
)}
{mode !== "phrases" && (
<div className="my-2">
<TensePicker vps={vps} onChange={adjustVps} mode={mode} />
</div>
)}
{mode === "phrases" && (
<VPDisplay VPS={vps} opts={props.opts} setForm={handleSetForm} />
)}
{mode === "charts" && (
<AllTensesDisplay
object={object}
VS={vps.verb}
opts={props.opts}
onChange={adjustVps}
/>
)}
{mode === "quiz" && <VPExplorerQuiz opts={props.opts} vps={vps} />}
{showClipped && (
<div
className="alert alert-primary text-center"
role="alert"
style={{
position: "fixed",
top: "30%",
left: "50%",
transform: "translate(-50%, -50%)",
zIndex: 9999999999999,
}}
>
{showClipped}
</div>
)}
{alertMsg && (
<div
className="alert alert-warning text-center"
role="alert"
style={{
position: "fixed",
top: "30%",
left: "50%",
transform: "translate(-50%, -50%)",
zIndex: 9999999999999,
}}
>
{alertMsg}
</div>
)}
</div>
);
}
export default VPExplorer;
@ -237,4 +269,3 @@ function getVPSFromUrl(): T.VPSelectionState | undefined {
const decoded = LZString.decompressFromEncodedURIComponent(fromParams);
return JSON.parse(decoded) as T.VPSelectionState;
}

View File

@ -6,9 +6,17 @@ import {
useRef,
useState,
} from "react";
import { getKingAndServant, renderVP } from "../../../lib/src/phrase-building/render-vp";
import { completeVPSelection, isPastTense } from "../../../lib/src/phrase-building/vp-tools";
import VPExplorerExplanationModal, { roleIcon } from "./VPExplorerExplanationModal";
import {
getKingAndServant,
renderVP,
} from "../../../lib/src/phrase-building/render-vp";
import {
completeVPSelection,
isPastTense,
} from "../../../lib/src/phrase-building/vp-tools";
import VPExplorerExplanationModal, {
roleIcon,
} from "./VPExplorerExplanationModal";
import APPicker from "../../src/ap-picker/APPicker";
// import autoAnimate from "@formkit/auto-animate";
import {
@ -18,23 +26,37 @@ import {
isNoObject,
} from "../../../lib/src/phrase-building/blocks-utils";
import ComplementPicker from "../ComplementPicker";
import { vpsReducer, VpsReducerAction } from "../../../lib/src/phrase-building/vps-reducer";
import {
vpsReducer,
VpsReducerAction,
} from "../../../lib/src/phrase-building/vps-reducer";
function VPPicker({ opts, vps, onChange, entryFeeder }: {
opts: T.TextOptions,
vps: T.VPSelectionState,
onChange: (vps: T.VPSelectionState) => void,
entryFeeder: T.EntryFeeder,
function VPPicker({
opts,
vps,
onChange,
entryFeeder,
}: {
opts: T.TextOptions;
vps: T.VPSelectionState;
onChange: (vps: T.VPSelectionState) => void;
entryFeeder: T.EntryFeeder;
}) {
const parent = useRef<HTMLDivElement>(null);
// useEffect(() => {
// parent.current && autoAnimate(parent.current);
// }, [parent]);
const [showingExplanation, setShowingExplanation] = useState<{ role: "servant" | "king", item: "subject" | "object" } | false>(false);
const [showingExplanation, setShowingExplanation] =
useState<{ role: "servant" | "king"; item: "subject" | "object" } | false>(
false
);
function adjustVps(action: VpsReducerAction) {
onChange(vpsReducer(vps, action));
}
function handleSubjectChange(subject: T.NPSelection | undefined, skipPronounConflictCheck?: boolean) {
function handleSubjectChange(
subject: T.NPSelection | undefined,
skipPronounConflictCheck?: boolean
) {
adjustVps({
type: "set subject",
payload: { subject, skipPronounConflictCheck },
@ -52,174 +74,333 @@ function VPPicker({ opts, vps, onChange, entryFeeder }: {
const phraseIsComplete = !!VPS;
const rendered = VPS ? renderVP(VPS) : undefined;
const servantIsShrunk = includesShrunkenServant(rendered?.kids);
const isPast = isPastTense(vps.verb.tenseCategory === "perfect" ? vps.verb.perfectTense : vps.verb.verbTense);
const isPast = isPastTense(
vps.verb.tenseCategory === "perfect"
? vps.verb.perfectTense
: vps.verb.verbTense
);
const roles = getKingAndServant(
isPast,
vps.verb.transitivity !== "intransitive",
vps.verb.transitivity !== "intransitive"
);
return <div>
<div className="clickable h5" onClick={() => adjustVps({ type: "insert new AP" })}>+ AP</div>
<div ref={parent} className="d-flex flex-row justify-content-around flex-wrap" style={{ marginLeft: "-0.5rem", marginRight: "-0.5rem" }}>
return (
<div>
<div
className="clickable h5"
onClick={() => adjustVps({ type: "insert new AP" })}
>
+ AP
</div>
<div
ref={parent}
className="d-flex flex-row justify-content-around flex-wrap"
style={{ marginLeft: "-0.5rem", marginRight: "-0.5rem" }}
>
{vps.blocks.map(({ block, key }, i, blocks) => {
if (isNoObject(block)) return null;
return <div className="my-2 card block-card p-1 mx-1" key={key} style={{
background: (servantIsShrunk && (
(roles.servant === "subject" && block?.type === "subjectSelection")
||
(roles.servant === "object" && block?.type === "objectSelection")
)) ? shrunkenBackground : "inherit",
}}>
<div className="d-flex flex-row justify-content-between mb-1" style={{ height: "1rem" }}>
{(i > 0 && !isNoObject(blocks[i - 1].block)) ? <div
return (
<div
className="my-2 card block-card p-1 mx-1"
key={key}
style={{
background:
servantIsShrunk &&
((roles.servant === "subject" &&
block?.type === "subjectSelection") ||
(roles.servant === "object" &&
block?.type === "objectSelection"))
? shrunkenBackground
: "inherit",
}}
>
<div
className="d-flex flex-row justify-content-between mb-1"
style={{ height: "1rem" }}
>
{i > 0 &&
!isNoObject(blocks[i - 1].block) &&
!isGenStatCompNoun(block) ? (
<div
className="small clickable ml-1"
onClick={() => adjustVps({ type: "shift block", payload: { index: i, direction: "back" }})}
onClick={() =>
adjustVps({
type: "shift block",
payload: { index: i, direction: "back" },
})
}
>
<i className="fas fa-chevron-left" />
</div> : <div/>}
{(i < vps.blocks.length - 1 && !isNoObject(blocks[i + 1].block)) ? <div
</div>
) : (
<div />
)}
{i < vps.blocks.length - 1 &&
!isNoObject(blocks[i + 1].block) &&
!isGenStatCompNoun(block) ? (
<div
className="small clickable mr-1"
onClick={() => adjustVps({ type: "shift block", payload: { index: i, direction: "forward" }})}
onClick={() =>
adjustVps({
type: "shift block",
payload: { index: i, direction: "forward" },
})
}
>
<i className="fas fa-chevron-right" />
</div> : <div/>}
</div>
{(!block || block.type === "AP")
? <APPicker
) : (
<div />
)}
</div>
{!block || block.type === "AP" ? (
<APPicker
phraseIsComplete={phraseIsComplete}
heading="AP"
entryFeeder={entryFeeder}
AP={block}
opts={opts}
onChange={AP => adjustVps({ type: "set AP", payload: { index: i, AP } })}
onChange={(AP) =>
adjustVps({ type: "set AP", payload: { index: i, AP } })
}
onRemove={() => adjustVps({ type: "remove AP", payload: i })}
/>
: (block?.type === "subjectSelection")
? <NPPicker
) : block?.type === "subjectSelection" ? (
<NPPicker
phraseIsComplete={phraseIsComplete}
heading={roles.king === "subject"
? <div className="h5 text-center">
Subj. <span onClick={() => setShowingExplanation({ role: "king", item: "subject" })}>{roleIcon.king}</span>
{(rendered && rendered.whatsAdjustable !== "servant") &&
heading={
roles.king === "subject" ? (
<div className="h5 text-center">
Subj.{" "}
<span
onClick={() =>
setShowingExplanation({
role: "king",
item: "subject",
})
}
>
{roleIcon.king}
</span>
{rendered && rendered.whatsAdjustable !== "servant" && (
<KingRemover
onChange={() => adjustVps({ type: "toggle king remove" })}
onChange={() =>
adjustVps({ type: "toggle king remove" })
}
showKing={!VPS?.form.removeKing}
/>
}
)}
</div>
: <div className="h5 text-center">
) : (
<div className="h5 text-center">
Subj.
{` `}
<span className="clickable" onClick={() => setShowingExplanation({ role: "servant", item: "subject" })}>{roleIcon.servant}</span>
<span
className="clickable"
onClick={() =>
setShowingExplanation({
role: "servant",
item: "subject",
})
}
>
{roleIcon.servant}
</span>
{` `}
{(rendered && rendered.whatsAdjustable !== "king") &&
{rendered && rendered.whatsAdjustable !== "king" && (
<ServantShrinker
shrunk={servantIsShrunk}
onClick={() => adjustVps({ type: "toggle servant shrink" })}
/>
onClick={() =>
adjustVps({ type: "toggle servant shrink" })
}
/>
)}
</div>
)
}
</div>}
entryFeeder={entryFeeder}
np={block.selection}
counterPart={vps.verb ? object : undefined}
role={(isPast && vps.verb.transitivity !== "intransitive")
role={
isPast && vps.verb.transitivity !== "intransitive"
? "ergative"
: "subject"
}
onChange={handleSubjectChange}
opts={opts}
isShrunk={(servantIsShrunk && roles.servant === "subject")}
isShrunk={servantIsShrunk && roles.servant === "subject"}
isRemoved={roles.king === "subject" && VPS?.form.removeKing}
/>
: (vps.verb && block?.type === "objectSelection" && block.selection !== "none")
? <div className="my-2" style={{ background: (servantIsShrunk && roles.servant === "object") ? shrunkenBackground : "inherit" }}>
{(typeof block.selection === "number")
? <div>
{roles.king === "object"
? <div className="h5 text-center clickable" onClick={() => setShowingExplanation({ role: "king", item: "object" })}>Object {roleIcon.king}</div>
: roles.servant === "object"
? <div className="h5 text-center clickable" onClick={() => setShowingExplanation({ role: "servant", item: "object" })}>Object {roleIcon.servant}</div>
: <div className="h5 text-center">Object</div>}
) : vps.verb &&
block?.type === "objectSelection" &&
block.selection !== "none" ? (
<div
className="my-2"
style={{
background:
servantIsShrunk && roles.servant === "object"
? shrunkenBackground
: "inherit",
}}
>
{typeof block.selection === "number" ? (
<div>
{roles.king === "object" ? (
<div
className="h5 text-center clickable"
onClick={() =>
setShowingExplanation({
role: "king",
item: "object",
})
}
>
Object {roleIcon.king}
</div>
) : roles.servant === "object" ? (
<div
className="h5 text-center clickable"
onClick={() =>
setShowingExplanation({
role: "servant",
item: "object",
})
}
>
Object {roleIcon.servant}
</div>
) : (
<div className="h5 text-center">Object</div>
)}
<div className="text-muted text-center">
<div className="mt-3 mb-1">Unspoken</div>
<div>3rd Pers. Masc. Plur.</div>
</div>
</div>
: <NPPicker
) : (
<NPPicker
phraseIsComplete={phraseIsComplete}
heading={roles.king === "object"
? <div className="h5 text-center">
Obj. <span onClick={() => setShowingExplanation({ role: "king", item: "object" })}>{roleIcon.king}</span>
{(rendered && rendered.whatsAdjustable !== "servant") &&
heading={
roles.king === "object" ? (
<div className="h5 text-center">
Obj.{" "}
<span
onClick={() =>
setShowingExplanation({
role: "king",
item: "object",
})
}
>
{roleIcon.king}
</span>
{rendered &&
rendered.whatsAdjustable !== "servant" && (
<KingRemover
onChange={() => adjustVps({ type: "toggle king remove" })}
onChange={() =>
adjustVps({ type: "toggle king remove" })
}
showKing={!VPS?.form.removeKing}
/>
}
)}
</div>
: <div className="h5 text-center">
) : (
<div className="h5 text-center">
Obj.
{` `}
<span className="clickable" onClick={() => setShowingExplanation({ role: "servant", item: "object" })}>{roleIcon.servant}</span>
{` `}
{(rendered && rendered.whatsAdjustable !== "king") &&
<ServantShrinker shrunk={servantIsShrunk} onClick={() => adjustVps({ type: "toggle servant shrink" })} />
<span
className="clickable"
onClick={() =>
setShowingExplanation({
role: "servant",
item: "object",
})
}
>
{roleIcon.servant}
</span>
{` `}
{rendered &&
rendered.whatsAdjustable !== "king" && (
<ServantShrinker
shrunk={servantIsShrunk}
onClick={() =>
adjustVps({ type: "toggle servant shrink" })
}
/>
)}
</div>
)
}
</div>}
entryFeeder={entryFeeder}
role="object"
np={block.selection}
counterPart={subject}
onChange={handleObjectChange}
opts={opts}
isShrunk={(servantIsShrunk && roles.servant === "object")}
isRemoved={roles.king === "object" && VPS?.form.removeKing}
/>}
isShrunk={servantIsShrunk && roles.servant === "object"}
isRemoved={
roles.king === "object" && VPS?.form.removeKing
}
/>
)}
</div>
: null}
</div>;
) : null}
</div>
);
})}
{vps.externalComplement && <div className="my-2 card block-card p-1 mr-1" key="complementPicker">
{vps.externalComplement && (
<div className="my-2 card block-card p-1 mr-1" key="complementPicker">
<div className="h5 text-center">Complement</div>
<ComplementPicker
phraseIsComplete={phraseIsComplete}
comp={vps.externalComplement.selection.type === "unselected"
comp={
vps.externalComplement.selection.type === "unselected"
? undefined
: vps.externalComplement as T.ComplementSelection // TODO: just typescript being dumb? - looks like it
: (vps.externalComplement as T.ComplementSelection) // TODO: just typescript being dumb? - looks like it
}
onChange={(payload) =>
adjustVps({ type: "set externalComplement", payload })
}
onChange={payload => adjustVps({ type: "set externalComplement", payload })}
opts={opts}
entryFeeder={entryFeeder}
/>
</div>}
</div>
)}
<div className="my-2">
<TensePicker
vps={vps}
onChange={adjustVps}
mode="phrases"
/>
<TensePicker vps={vps} onChange={adjustVps} mode="phrases" />
</div>
</div>
<VPExplorerExplanationModal
showing={showingExplanation}
setShowing={setShowingExplanation}
/>
</div>;
</div>
);
}
function ServantShrinker({ shrunk, onClick }: {
function ServantShrinker({
shrunk,
onClick,
}: {
shrunk: boolean;
onClick: () => void;
}) {
return <span className="mx-2 clickable" onClick={onClick}>
return (
<span className="mx-2 clickable" onClick={onClick}>
{!shrunk ? "🪄" : "👶"}
</span>;
</span>
);
}
function KingRemover({ showKing, onChange }: {
function KingRemover({
showKing,
onChange,
}: {
showKing: boolean;
onChange: () => void;
}) {
return <span className="form-check form-check-inline ml-3">
return (
<span className="form-check form-check-inline ml-3">
<input
checked={showKing}
onChange={onChange}
@ -227,7 +408,29 @@ function KingRemover({ showKing, onChange }: {
type="checkbox"
id="showKingCheck"
/>
</span>;
</span>
);
}
function isGenStatCompNoun(
block:
| T.APSelection
| T.ComplementSelection
| T.SubjectSelection
| T.ObjectSelection
| undefined
) {
if (!block) return false;
console.log({ block });
if (
block.type === "objectSelection" &&
typeof block.selection === "object" &&
block.selection.selection.type === "noun" &&
block.selection.selection.genStativeComplement
) {
return true;
}
return false;
}
export default VPPicker;

View File

@ -1,31 +1,48 @@
import * as T from "../../../types";
import ButtonSelect from "../ButtonSelect";
import { RootsAndStems } from "../verb-info/VerbInfo";
import { getAbilityRootsAndStems, getPassiveRootsAndStems, getVerbInfo } from "../../../lib/src/verb-info";
import {
getAbilityRootsAndStems,
getPassiveRootsAndStems,
getVerbInfo,
} from "../../../lib/src/verb-info";
import Hider from "../Hider";
import useStickyState from "../useStickyState";
import CompoundDisplay from "./CompoundDisplay";
import {
VpsReducerAction
} from "../../../lib/src/phrase-building/vps-reducer";
import { VpsReducerAction } from "../../../lib/src/phrase-building/vps-reducer";
import { ensureNonComboVerbInfo } from "../../../lib/src/misc-helpers";
// TODO: dark on past tense selecitons
function VerbPicker(props: {
vps: T.VPSelectionState,
onChange: (a: VpsReducerAction) => void,
opts: T.TextOptions,
handleLinkClick: ((ts: number) => void) | "none",
vps: T.VPSelectionState;
onChange: (a: VpsReducerAction) => void;
opts: T.TextOptions;
handleLinkClick: ((ts: number) => void) | "none";
}) {
const [showRootsAndStems, setShowRootsAndStems] = useStickyState<boolean>(false, "showRootsAndStems");
const infoRaw = props.vps.verb ? getVerbInfo(props.vps.verb.verb.entry, props.vps.verb.verb.complement) : undefined;
const info = (!infoRaw || !props.vps.verb)
const [showRootsAndStems, setShowRootsAndStems] = useStickyState<boolean>(
false,
"showRootsAndStems"
);
const infoRaw = props.vps.verb
? getVerbInfo(props.vps.verb.verb.entry, props.vps.verb.verb.complement)
: undefined;
const info =
!infoRaw || !props.vps.verb
? undefined
: ("stative" in infoRaw)
? infoRaw[props.vps.verb.isCompound === "stative" ? "stative" : "dynamic"]
: ("transitive" in infoRaw)
? infoRaw[props.vps.verb.transitivity === "grammatically transitive" ? "grammaticallyTransitive" : "transitive"]
: "stative" in infoRaw
? infoRaw[
props.vps.verb.isCompound === "stative" ||
props.vps.verb.isCompound === "generative stative"
? "stative"
: "dynamic"
]
: "transitive" in infoRaw
? infoRaw[
props.vps.verb.transitivity === "grammatically transitive"
? "grammaticallyTransitive"
: "transitive"
]
: infoRaw;
if (info && ("stative" in info || "transitive" in info)) {
return <div>ERROR: Verb version should be select first</div>;
@ -36,10 +53,14 @@ function VerbPicker(props: {
payload: value,
});
}
function notInstransitive(t: "transitive" | "intransitive" | "grammatically transitive"): "transitive" | "grammatically transitive" {
function notInstransitive(
t: "transitive" | "intransitive" | "grammatically transitive"
): "transitive" | "grammatically transitive" {
return t === "intransitive" ? "transitive" : t;
}
function handleChangeTransitivity(payload: "transitive" | "grammatically transitive") {
function handleChangeTransitivity(
payload: "transitive" | "grammatically transitive"
) {
props.onChange({
type: "set transitivity",
payload,
@ -51,91 +72,142 @@ function VerbPicker(props: {
payload,
});
}
const passiveRootsAndStems = (info && props.vps.verb.voice === "passive") ? getPassiveRootsAndStems(info) : undefined;
const passiveRootsAndStems =
info && props.vps.verb.voice === "passive"
? getPassiveRootsAndStems(info)
: undefined;
const abilityRootsAndStems = (() => {
try {
return (info && props.vps.verb.tenseCategory === "modal") ? getAbilityRootsAndStems(info) : undefined;
return info && props.vps.verb.tenseCategory === "modal"
? getAbilityRootsAndStems(info)
: undefined;
} catch (e) {
console.log("error making ability roots and stems", e);
return undefined;
}
})();
return <div className="mb-3">
{info && <CompoundDisplay
return (
<div className="mb-3">
{info && (
<CompoundDisplay
info={info}
opts={props.opts}
handleLinkClick={props.handleLinkClick}
/>}
{info && <div className="mt-3 mb-1 text-center">
/>
)}
{info && (
<div className="mt-3 mb-1 text-center">
<Hider
showing={showRootsAndStems}
label={`🌳 ${passiveRootsAndStems ? "Passive" : abilityRootsAndStems ? "Ability" : ""} Roots and Stems${info.type === "dynamic compound" ? " for Aux. Verb" : ""}`}
handleChange={() => setShowRootsAndStems(p => !p)}
label={`🌳 ${
passiveRootsAndStems
? "Passive"
: abilityRootsAndStems
? "Ability"
: ""
} Roots and Stems${
info.type === "dynamic compound" ? " for Aux. Verb" : ""
}`}
handleChange={() => setShowRootsAndStems((p) => !p)}
hLevel={5}
>
<RootsAndStems
textOptions={props.opts}
info={passiveRootsAndStems
info={
passiveRootsAndStems
? passiveRootsAndStems
: abilityRootsAndStems
? abilityRootsAndStems
: info.type === "dynamic compound"
? ensureNonComboVerbInfo(getVerbInfo(info.auxVerb))
: info}
: info
}
/>
</Hider>
</div>}
<div className="d-flex flex-row justify-content-around flex-wrap" style={{ maxWidth: "400px", margin: "0 auto" }}>
{props.vps.verb && props.vps.verb.canChangeTransitivity && <div className="text-center my-2">
</div>
)}
<div
className="d-flex flex-row justify-content-around flex-wrap"
style={{ maxWidth: "400px", margin: "0 auto" }}
>
{props.vps.verb && props.vps.verb.canChangeTransitivity && (
<div className="text-center my-2">
<ButtonSelect
small
options={[{
options={[
{
label: "gramm. trans.",
value: "grammatically transitive",
}, {
},
{
label: "trans.",
value: "transitive",
}]}
},
]}
value={notInstransitive(props.vps.verb.transitivity)}
handleChange={handleChangeTransitivity}
/>
</div>}
{props.vps.verb && props.vps.verb.canChangeVoice && <div className="text-center my-2">
</div>
)}
{props.vps.verb && props.vps.verb.canChangeVoice && (
<div className="text-center my-2">
<ButtonSelect
small
value={props.vps.verb.voice}
options={(props.vps.verb.tenseCategory === "imperative") // || props.vps.verb.tenseCategory === "modal")
? [{
options={
props.vps.verb.tenseCategory === "imperative" // || props.vps.verb.tenseCategory === "modal")
? [
{
label: "Active",
value: "active",
}]
: [{
},
]
: [
{
label: "Active",
value: "active",
}, {
},
{
label: "Passive",
value: "passive",
}]}
},
]
}
handleChange={onVoiceSelect}
/>
</div>}
{props.vps.verb && props.vps.verb.canChangeStatDyn && <div className="text-center my-2">
</div>
)}
{props.vps.verb && props.vps.verb.canChangeStatDyn && (
<div className="text-center my-2">
<ButtonSelect
small
options={[{
label: "stative",
options={[
{
label:
infoRaw?.type === "dynamic or generative stative compound"
? "gen. stative"
: "stative",
value: "stative",
}, {
},
{
label: "dynamic",
value: "dynamic",
}]}
value={props.vps.verb.isCompound ? props.vps.verb.isCompound : "stative"}
},
]}
value={
props.vps.verb.isCompound === "generative stative"
? "stative"
: props.vps.verb.isCompound
? props.vps.verb.isCompound
: "stative"
}
handleChange={handleChangeStatDyn}
/>
</div>}
</div>
</div>;
)}
</div>
</div>
);
}
export default VerbPicker;

View File

@ -1,7 +1,8 @@
import { renderVerb } from "../../../lib/src/new-verb-engine/render-verb";
import * as T from "../../../types";
import { equals } from "rambda";
import { isPastTense } from "../../library";
import { isPastTense, renderNPSelection } from "../../library";
import { getPersonFromNP } from "../../../lib/src/phrase-building/vp-tools";
export function buildVerbChart({
verb,
@ -10,6 +11,7 @@ export function buildVerbChart({
voice,
imperative,
negative,
objectNP,
}: {
verb: T.VerbEntry;
tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense;
@ -17,7 +19,11 @@ export function buildVerbChart({
imperative: boolean;
voice: T.VerbSelection["voice"];
negative: boolean;
}): T.OptionalPersonInflections<T.SingleOrLengthOpts<T.RenderVerbOutput[]>> {
objectNP: T.NPSelection | undefined;
}): {
objNP: T.Rendered<T.NPSelection> | undefined;
vbs: T.OptionalPersonInflections<T.SingleOrLengthOpts<T.RenderVerbOutput[]>>;
} {
const allPersons = imperative
? [
T.Person.SecondSingMale,
@ -27,6 +33,9 @@ export function buildVerbChart({
]
: ([...Array(12).keys()] as T.Person[]);
const isPast = isPastTense(tense);
const objNP: T.Rendered<T.NPSelection> | undefined = objectNP
? renderNPSelection(objectNP, false, false, "object", "none", false)
: undefined;
function conjugateAllPers(
p?: T.Person
): T.SingleOrLengthOpts<T.RenderVerbOutput[]> {
@ -46,19 +55,51 @@ export function buildVerbChart({
negative,
});
});
const hasLengths = vIsLength("long")(ps[0]);
const hasMini = vIsLength("mini")(ps[0]);
return pullOutLengths(hasLengths, hasMini, ps);
return pullOutLengths(ps);
}
if (transitivity === "transitive" && !isPast) {
return conflateIfNoCompGenNumDiff({
return {
objNP,
vbs: conflateIfNoCompGenNumDiff({
mascSing: conjugateAllPers(T.Person.FirstSingMale),
mascPlur: conjugateAllPers(T.Person.FirstPlurMale),
femSing: conjugateAllPers(T.Person.FirstSingFemale),
femPlur: conjugateAllPers(T.Person.FirstPlurFemale),
});
}),
};
} else if (objNP && isPast) {
return {
objNP,
vbs: pullOutLengths([
renderVerb({
verb,
tense,
subject: 0,
object: getPersonFromNP(objNP),
voice,
negative,
}),
]),
};
} else if (isPast && transitivity === "grammatically transitive") {
return {
objNP,
vbs: pullOutLengths([
renderVerb({
verb,
tense,
subject: 0,
object: T.Person.ThirdPlurMale,
voice,
negative,
}),
]),
};
} else {
return conjugateAllPers();
return {
objNP: undefined,
vbs: conjugateAllPers(),
};
}
}
@ -106,23 +147,21 @@ function grabLength(
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[]> {
const hasLengths = vIsLength("long")(ps[0]);
const hasMini = vIsLength("mini")(ps[0]);
if (!hasLengths) {
return ps;
}

View File

@ -0,0 +1,5 @@
@media screen and (max-width: 600px) {
.hide-on-mobile {
display: none;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,85 +0,0 @@
import * as T from "../../types";
export function fmapSingleOrLengthOpts<A extends object, B extends object>(f: (x: A) => B, x: T.SingleOrLengthOpts<A>): T.SingleOrLengthOpts<B> {
if ("long" in x) {
return {
long: f(x.long),
short: f(x.short),
..."mini" in x && x.mini ? {
mini: f(x.mini),
} : {},
};
} else {
return f(x);
}
}
export function mapInflections(f: (x: T.PsString) => T.PsString, inf: T.UnisexInflections): T.UnisexInflections {
function handleSide(inf: T.InflectionSet): T.InflectionSet {
return inf.map(x => x.map(f)) as T.ArrayFixed<T.ArrayOneOrMore<T.PsString>, 3>;
}
return {
masc: handleSide(inf.masc),
fem: handleSide(inf.fem),
};
}
export function mapVerbRenderedOutput(f: (a: T.PsString) => T.PsString, [a, b]: T.VerbRenderedOutput): T.VerbRenderedOutput {
return [
fmapVHead(a),
fmapV(b),
];
function fmapVHead([v]: [T.VHead] | []): [T.VHead] | [] {
if (v === undefined) {
return [];
}
if (v.type === "PH") {
return [{
...v,
ps: f(v.ps),
}];
}
return [{
...v,
comp: fmapComp(v.comp),
}];
}
function fmapComp(comp: T.Comp): T.Comp {
return {
...comp,
ps: f(comp.ps),
};
}
function fmapV(v: [T.VB, T.VBE] | [T.VBE]): [T.VB, T.VBE]| [T.VBE] {
return v.map(fmapVB) as [T.VB, T.VBE]| [T.VBE];
}
function fmapVB<V extends T.VB | T.VBE>(v: V): V {
if (v.type === "welded") {
return {
...v,
left: fmapWeldedLeft(v.left),
right: fmapVB(v.right),
};
}
return {
...v,
ps: fmapSingleOrLengthOpts((x) => x.map(f), v.ps),
};
}
function fmapWeldedLeft(v: T.NComp | T.VBBasic | T.Welded) {
if (v.type === "NComp") {
return {
...v,
comp: fmapComp(v.comp),
};
}
return fmapVB(v);
}
}

View File

@ -41,3 +41,95 @@ export const monoidPsStringWVars: Monoid<T.PsString[]> = {
concat: semigroupPsStringWVars.concat,
empty: [monoidPsString.empty],
};
export function fmapSingleOrLengthOpts<A extends object, B extends object>(
f: (x: A) => B,
x: T.SingleOrLengthOpts<A>
): T.SingleOrLengthOpts<B> {
if ("long" in x) {
return {
long: f(x.long),
short: f(x.short),
...("mini" in x && x.mini
? {
mini: f(x.mini),
}
: {}),
};
} else {
return f(x);
}
}
export function mapInflections(
f: (x: T.PsString) => T.PsString,
inf: T.UnisexInflections
): T.UnisexInflections {
function handleSide(inf: T.InflectionSet): T.InflectionSet {
return inf.map((x) => x.map(f)) as T.ArrayFixed<
T.ArrayOneOrMore<T.PsString>,
3
>;
}
return {
masc: handleSide(inf.masc),
fem: handleSide(inf.fem),
};
}
export function mapVerbRenderedOutput(
f: (a: T.PsString) => T.PsString,
[a, b]: T.VerbRenderedOutput
): T.VerbRenderedOutput {
return [fmapVHead(a), fmapV(b)];
function fmapVHead([v]: [T.VHead] | []): [T.VHead] | [] {
if (v === undefined) {
return [];
}
if (v.type === "PH") {
return [
{
...v,
ps: f(v.ps),
},
];
}
return [
{
...v,
comp: fmapComp(v.comp),
},
];
}
function fmapComp(comp: T.Comp): T.Comp {
return {
...comp,
ps: f(comp.ps),
};
}
function fmapV(v: [T.VB, T.VBE] | [T.VBE]): [T.VB, T.VBE] | [T.VBE] {
return v.map(fmapVB) as [T.VB, T.VBE] | [T.VBE];
}
function fmapVB<V extends T.VB | T.VBE>(v: V): V {
if (v.type === "welded") {
return {
...v,
left: fmapWeldedLeft(v.left),
right: fmapVB(v.right),
};
}
return {
...v,
ps: fmapSingleOrLengthOpts((x) => x.map(f), v.ps),
};
}
function fmapWeldedLeft(v: T.NComp | T.VBBasic | T.Welded) {
if (v.type === "NComp") {
return {
...v,
comp: fmapComp(v.comp),
};
}
return fmapVB(v);
}
}

View File

@ -7,7 +7,7 @@
*/
import * as T from "../../types";
import { fmapSingleOrLengthOpts } from "./fmaps";
import { fmapSingleOrLengthOpts } from "./fp-ps";
export const blank: T.PsString = {
p: "_____",

View File

@ -5,7 +5,7 @@ import {
personNumber,
personToGenNum,
} from "../misc-helpers";
import { fmapSingleOrLengthOpts } from "../fmaps";
import { fmapSingleOrLengthOpts } from "../fp-ps";
import { concatPsString, getLength } from "../p-text-helpers";
import {
presentEndings,
@ -18,6 +18,7 @@ import {
isAbilityTense,
isPerfectTense,
isTlulVerb,
isImperativeTense,
} from "../type-predicates";
import { perfectTenseHasBa } from "../phrase-building/vp-tools";
import { makePsString, removeFVarients } from "../accent-and-ps-utils";
@ -100,6 +101,7 @@ export function renderVerb({
subject,
object,
voice,
negative,
}: {
verb: T.VerbEntry;
negative: boolean;
@ -126,7 +128,7 @@ export function renderVerb({
const [vHead, rest] = getRootStem({
verb,
rs: isPast ? "root" : "stem",
aspect,
aspect: negative && isImperativeTense(tense) ? "imperfective" : aspect,
voice,
type,
genderNumber: personToGenNum(transitive ? object : subject),
@ -136,7 +138,6 @@ export function renderVerb({
const ending = getEnding(king, tenseC, aspect);
return {
hasBa,
objComp: undefined,
vbs: [
vHead,
addEnding({

View File

@ -31,9 +31,9 @@ import {
isKedul,
} from "./rs-helpers";
import { inflectPattern3 } from "./new-inflectors";
import { fmapSingleOrLengthOpts } from "../fmaps";
import { fmapSingleOrLengthOpts } from "../fp-ps";
const statVerb = {
export const statVerb = {
intransitive: vEntry({
ts: 1581086654898,
i: 11100,

View File

@ -30,7 +30,7 @@ import {
splitUpSyllables,
} from "./accent-helpers";
import * as T from "../../types";
import { fmapSingleOrLengthOpts } from "./fmaps";
import { fmapSingleOrLengthOpts } from "./fp-ps";
const endingInSingleARegex = /[^a]'??[aá]'??$/;
const endingInHeyOrAynRegex = /[^ا][هع]$/;
@ -42,24 +42,29 @@ export function inflectWord(word: T.DictionaryEntry): T.InflectorOutput {
const w = removeFVarients(word);
if (w.c?.includes("doub.")) {
const words = splitDoubleWord(w);
const inflected = words.map((x) => ensureUnisexInflections(inflectWord(x), x));
const inflected = words.map((x) =>
ensureUnisexInflections(inflectWord(x), x)
);
return {
inflections: concatInflections(
inflected[0].inflections,
inflected[1].inflections,
inflected[1].inflections
) as T.UnisexInflections,
};
}
if (w.c && w.c.includes("pl.")) {
return handlePluralNounOrAdj(w);
}
if (w.c && (w.c.includes("adj.") || w.c.includes("unisex") || w.c.includes("num"))) {
if (
w.c &&
(w.c.includes("adj.") || w.c.includes("unisex") || w.c.includes("num"))
) {
return handleUnisexWord(w);
}
if (w.c && (w.c.includes("n. m."))) {
if (w.c && w.c.includes("n. m.")) {
return handleMascNoun(w);
}
if (w.c && (w.c.includes("n. f."))) {
if (w.c && w.c.includes("n. f.")) {
return handleFemNoun(w);
}
// It's not a noun/adj
@ -78,8 +83,8 @@ function handleUnisexWord(word: T.DictionaryEntryNoFVars): T.InflectorOutput {
if (word.infap && word.infaf && word.infbp && word.infbf) {
return {
inflections: inflectIrregularUnisex(word.p, word.f, [
{p: word.infap, f: word.infaf},
{p: word.infbp, f: word.infbf},
{ p: word.infap, f: word.infaf },
{ p: word.infbp, f: word.infbf },
]),
...plurals,
};
@ -88,10 +93,16 @@ function handleUnisexWord(word: T.DictionaryEntryNoFVars): T.InflectorOutput {
return { inflections: inflectRegularYeyUnisex(word.p, word.f), ...plurals };
}
if (pEnd === "ه" && word.g.slice(-1) === "u") {
return { inflections: inflectRegularShwaEndingUnisex(word.p, word.f), ...plurals };
return {
inflections: inflectRegularShwaEndingUnisex(word.p, word.f),
...plurals,
};
}
if (pEnd === "ی" && word.f.slice(-2) === "éy") {
return { inflections: inflectEmphasizedYeyUnisex(word.p, word.f), ...plurals };
return {
inflections: inflectEmphasizedYeyUnisex(word.p, word.f),
...plurals,
};
}
if (
pashtoConsonants.includes(pEnd) ||
@ -100,7 +111,10 @@ function handleUnisexWord(word: T.DictionaryEntryNoFVars): T.InflectorOutput {
word.f.slice(-1) === "w" ||
(word.p.slice(-1) === "ه" && word.f.slice(-1) === "h")
) {
return { inflections: inflectConsonantEndingUnisex(word.p, word.f), ...plurals };
return {
inflections: inflectConsonantEndingUnisex(word.p, word.f),
...plurals,
};
}
if (plurals) return plurals;
return false;
@ -128,13 +142,16 @@ function handleMascNoun(w: T.DictionaryEntryNoFVars): T.InflectorOutput {
if (w.infap && w.infaf && w.infbp && w.infbf) {
return {
inflections: inflectIrregularMasc(w.p, w.f, [
{p: w.infap, f: w.infaf},
{p: w.infbp, f: w.infbf},
{ p: w.infap, f: w.infaf },
{ p: w.infbp, f: w.infbf },
]),
...plurals,
};
}
const isTobEnding = (w.p.slice(-3) === "توب" && ["tób", "tob"].includes(w.f.slice(-3)) && w.p.length > 3);
const isTobEnding =
w.p.slice(-3) === "توب" &&
["tób", "tob"].includes(w.f.slice(-3)) &&
w.p.length > 3;
if (isTobEnding) {
return { inflections: inflectTobMasc(w.p, w.f), ...plurals };
}
@ -142,9 +159,12 @@ function handleMascNoun(w: T.DictionaryEntryNoFVars): T.InflectorOutput {
return { inflections: inflectRegularYeyMasc(w.p, w.f), ...plurals };
}
if (pEnd === "ی" && fEnd === "éy") {
return { inflections: inflectRegularEmphasizedYeyMasc(w.p, w.f), ...plurals };
return {
inflections: inflectRegularEmphasizedYeyMasc(w.p, w.f),
...plurals,
};
}
return plurals ? { ...plurals } : false
return plurals ? { ...plurals } : false;
}
function handleFemNoun(word: T.DictionaryEntryNoFVars): T.InflectorOutput {
@ -163,13 +183,22 @@ function handleFemNoun(word: T.DictionaryEntryNoFVars): T.InflectorOutput {
return { inflections: inflectRegularAFem(word.p, word.f), ...plurals };
}
if (word.p.slice(-1) === "ح" && endingInSingleARegex.test(word.f)) {
return { inflections: inflectRegularAWithHimPEnding(word.p, word.f), ...plurals };
return {
inflections: inflectRegularAWithHimPEnding(word.p, word.f),
...plurals,
};
}
// TODO: better reusable function to check if something ends with a consonant
if ((pashtoConsonants.includes(pEnd) || word.f.slice(-1) === "w") && !animate) {
return { inflections: inflectRegularInanMissingAFem(word.p, word.f), ...plurals };
if (
(pashtoConsonants.includes(pEnd) || word.f.slice(-1) === "w") &&
!animate
) {
return {
inflections: inflectRegularInanMissingAFem(word.p, word.f),
...plurals,
};
}
if (pEnd === "ي" && (!animate)) {
if (pEnd === "ي" && !animate) {
return { inflections: inflectRegularInanEeFem(word.p, word.f), ...plurals };
}
if (pEnd === "ۍ") {
@ -182,59 +211,74 @@ function handleFemNoun(word: T.DictionaryEntryNoFVars): T.InflectorOutput {
}
// LEVEL 3 FUNCTIONS
function inflectIrregularUnisex(p: string, f: string, inflections: Array<{p: string, f: string}>): T.Inflections {
function inflectIrregularUnisex(
p: string,
f: string,
inflections: Array<{ p: string; f: string }>
): T.Inflections {
const inf1 = removeAccents(inflections[1]);
const inf0 = removeAccents(inflections[0]);
const inf0fSyls = splitUpSyllables(inf0.f).length;
return {
masc: [
[{p, f}],
[{p: inflections[0].p, f: `${inf0.f.slice(0, -1)}${inf0fSyls === 1 ? "u" : "ú"}` }],
[{p: `${inf1.p}و`, f: `${inf1.f}${inf0fSyls === 1 ? "o" : "ó"}`}],
[{ p, f }],
[
{
p: inflections[0].p,
f: `${inf0.f.slice(0, -1)}${inf0fSyls === 1 ? "u" : "ú"}`,
},
],
[{ p: `${inf1.p}و`, f: `${inf1.f}${inf0fSyls === 1 ? "o" : "ó"}` }],
],
fem: [
[{p: `${inf1.p}ه`, f: `${inf1.f}${inf0fSyls === 1 ? "a" : "á"}`}],
[{p: `${inf1.p}ې`, f: `${inf1.f}${inf0fSyls === 1 ? "e" : "é"}`}],
[{p: `${inf1.p}و`, f: `${inf1.f}${inf0fSyls === 1 ? "o" : "ó"}`}],
[{ p: `${inf1.p}ه`, f: `${inf1.f}${inf0fSyls === 1 ? "a" : "á"}` }],
[{ p: `${inf1.p}ې`, f: `${inf1.f}${inf0fSyls === 1 ? "e" : "é"}` }],
[{ p: `${inf1.p}و`, f: `${inf1.f}${inf0fSyls === 1 ? "o" : "ó"}` }],
],
};
}
export function inflectRegularYeyUnisex(p: string, f: string): T.UnisexInflections {
export function inflectRegularYeyUnisex(
p: string,
f: string
): T.UnisexInflections {
const baseP = p.slice(0, -1);
const baseF = f.slice(0, -2);
return {
masc: [
[{p, f}],
[{p: `${baseP}ي`, f: `${baseF}ee`}],
[{ p, f }],
[{ p: `${baseP}ي`, f: `${baseF}ee` }],
[
{p: `${baseP}یو`, f: `${baseF}iyo`},
{p: `${baseP}و`, f: `${baseF}o`},
{ p: `${baseP}یو`, f: `${baseF}iyo` },
{ p: `${baseP}و`, f: `${baseF}o` },
],
],
fem: [
[{p: `${baseP}ې`, f: `${baseF}e`}],
[{p: `${baseP}ې`, f: `${baseF}e`}],
[{p: `${baseP}و`, f: `${baseF}o`}],
[{ p: `${baseP}ې`, f: `${baseF}e` }],
[{ p: `${baseP}ې`, f: `${baseF}e` }],
[{ p: `${baseP}و`, f: `${baseF}o` }],
],
};
}
export function inflectRegularShwaEndingUnisex(pr: string, fr: string): T.UnisexInflections {
export function inflectRegularShwaEndingUnisex(
pr: string,
fr: string
): T.UnisexInflections {
const { p, f } = removeAccents(makePsString(pr, fr));
const accented = fr.slice(-1) === "ú";
const baseP = p.slice(0, -1);
const baseF = f.slice(0, -1);
return {
masc: [
[{p: `${baseP}ه`, f: `${baseF}${accented ? "ú" : "u"}`}],
[{p: `${baseP}ه`, f: `${baseF}${accented ? "ú" : "u"}`}],
[{p: `${baseP}و`, f: `${baseF}${accented ? "ó" : "o"}`}],
[{ p: `${baseP}ه`, f: `${baseF}${accented ? "ú" : "u"}` }],
[{ p: `${baseP}ه`, f: `${baseF}${accented ? "ú" : "u"}` }],
[{ p: `${baseP}و`, f: `${baseF}${accented ? "ó" : "o"}` }],
],
fem: [
[{p: `${baseP}ه`, f: `${baseF}${accented ? "á" : "a"}`}],
[{p: `${baseP}ې`, f: `${baseF}${accented ? "é" : "e"}`}],
[{p: `${baseP}و`, f: `${baseF}${accented ? "ó" : "o"}`}],
[{ p: `${baseP}ه`, f: `${baseF}${accented ? "á" : "a"}` }],
[{ p: `${baseP}ې`, f: `${baseF}${accented ? "é" : "e"}` }],
[{ p: `${baseP}و`, f: `${baseF}${accented ? "ó" : "o"}` }],
],
};
}
@ -244,39 +288,39 @@ function inflectEmphasizedYeyUnisex(p: string, f: string): T.UnisexInflections {
const baseF = f.slice(0, -2);
return {
masc: [
[{p, f}],
[{p: `${baseP}ي`, f: `${baseF}ée`}],
[{ p, f }],
[{ p: `${baseP}ي`, f: `${baseF}ée` }],
[
{p: `${baseP}یو`, f: `${baseF}iyo`},
{p: `${baseP}و`, f: `${baseF}ó`},
{ p: `${baseP}یو`, f: `${baseF}iyo` },
{ p: `${baseP}و`, f: `${baseF}ó` },
],
],
fem: [
[{p: `${baseP}ۍ`, f: `${baseF}úy`}],
[{p: `${baseP}ۍ`, f: `${baseF}úy`}],
[{ p: `${baseP}ۍ`, f: `${baseF}úy` }],
[{ p: `${baseP}ۍ`, f: `${baseF}úy` }],
[
{ p: `${baseP}یو`, f: `${baseF}úyo` },
{ p: `${baseP}و`, f: `${baseF}ó`, },
{ p: `${baseP}و`, f: `${baseF}ó` },
],
],
};
}
function inflectConsonantEndingUnisex(p: string, f: string): T.UnisexInflections {
function inflectConsonantEndingUnisex(
p: string,
f: string
): T.UnisexInflections {
const fSyls = splitUpSyllables(removeAccents(f));
const iBase = fSyls.length === 1
const iBase =
fSyls.length === 1
? makePsString(p, accentFSylsOnNFromEnd(fSyls, 0))
: makePsString(p, f);
return {
masc: [
[{p, f}],
[{p, f}],
[{p: `${iBase.p}و`, f: `${iBase.f}o`}],
],
masc: [[{ p, f }], [{ p, f }], [{ p: `${iBase.p}و`, f: `${iBase.f}o` }]],
fem: [
[{p: `${iBase.p}ه`, f: `${iBase.f}a`}],
[{p: `${iBase.p}ې`, f: `${iBase.f}e`}],
[{p: `${iBase.p}و`, f: `${iBase.f}o`}],
[{ p: `${iBase.p}ه`, f: `${iBase.f}a` }],
[{ p: `${iBase.p}ې`, f: `${iBase.f}e` }],
[{ p: `${iBase.p}و`, f: `${iBase.f}o` }],
],
};
}
@ -286,11 +330,11 @@ function inflectRegularYeyMasc(p: string, f: string): T.Inflections {
const baseF = f.slice(0, -2);
return {
masc: [
[{p, f}],
[{p: `${baseP}ي`, f: `${baseF}ee`}],
[{ p, f }],
[{ p: `${baseP}ي`, f: `${baseF}ee` }],
[
{p: `${baseP}یو`, f: `${baseF}iyo`},
{p: `${baseP}و`, f: `${baseF}o`},
{ p: `${baseP}یو`, f: `${baseF}iyo` },
{ p: `${baseP}و`, f: `${baseF}o` },
],
],
};
@ -301,9 +345,9 @@ function inflectTobMasc(p: string, f: string): T.Inflections {
const baseF = f.slice(0, -3);
return {
masc: [
[{p, f}],
[{p: `${baseP}تابه`, f: `${baseF}taabu`}],
[{p: `${baseP}تبو`, f: `${baseF}tabo`}],
[{ p, f }],
[{ p: `${baseP}تابه`, f: `${baseF}taabu` }],
[{ p: `${baseP}تبو`, f: `${baseF}tabo` }],
],
};
}
@ -313,39 +357,55 @@ function inflectRegularEmphasizedYeyMasc(p: string, f: string): T.Inflections {
const baseF = f.slice(0, -2);
return {
masc: [
[{p, f}],
[{p: `${baseP}ي`, f: `${baseF}ée`}],
[{ p, f }],
[{ p: `${baseP}ي`, f: `${baseF}ée` }],
[
{p: `${baseP}یو`, f: `${baseF}iyo`},
{p: `${baseP}و`, f: `${baseF}o`},
{ p: `${baseP}یو`, f: `${baseF}iyo` },
{ p: `${baseP}و`, f: `${baseF}o` },
],
],
};
}
function inflectIrregularMasc(p: string, f: string, inflections: Array<{p: string, f: string}>): T.Inflections {
function inflectIrregularMasc(
p: string,
f: string,
inflections: Array<{ p: string; f: string }>
): T.Inflections {
let inf0f = removeAccents(inflections[0].f);
const inf0syls = splitUpSyllables(f).length;
const inf1f = removeAccents(inflections[1].f);
return {
masc: [
[{p, f}],
[{p: inflections[0].p, f: `${inf0f.slice(0, -1)}${inf0syls === 1 ? "u" : "ú"}`}],
[{p: `${inflections[1].p}و`, f: `${inf1f}${inf0syls === 1 ? "o" : "ó"}`}],
[{ p, f }],
[
{
p: inflections[0].p,
f: `${inf0f.slice(0, -1)}${inf0syls === 1 ? "u" : "ú"}`,
},
],
[
{
p: `${inflections[1].p}و`,
f: `${inf1f}${inf0syls === 1 ? "o" : "ó"}`,
},
],
],
};
}
function inflectRegularAFem(p: string, f: string): T.Inflections {
const withoutTrailingComma = ["'", ""].includes(f.slice(-1)) ? f.slice(0, -1) : f;
const withoutTrailingComma = ["'", ""].includes(f.slice(-1))
? f.slice(0, -1)
: f;
const accentLast = hasAccents(withoutTrailingComma.slice(-1));
const baseF = withoutTrailingComma.slice(0, -1);
const baseP = p.slice(-1) === "ع" ? p : p.slice(0, -1);
return {
fem: [
[{p, f}],
[{p: `${baseP}ې`, f: `${baseF}${accentLast ? "é" : "e"}`}],
[{p: `${baseP}و`, f: `${baseF}${accentLast ? "ó" : "o"}`}],
[{ p, f }],
[{ p: `${baseP}ې`, f: `${baseF}${accentLast ? "é" : "e"}` }],
[{ p: `${baseP}و`, f: `${baseF}${accentLast ? "ó" : "o"}` }],
],
};
}
@ -354,22 +414,21 @@ function inflectRegularAWithHimPEnding(p: string, f: string): T.Inflections {
const baseF = f.slice(0, -1);
return {
fem: [
[{p, f}],
[{p: `${p}ې`, f: `${baseF}e`}],
[{p: `${p}و`, f: `${baseF}o`}],
[{ p, f }],
[{ p: `${p}ې`, f: `${baseF}e` }],
[{ p: `${p}و`, f: `${baseF}o` }],
],
};
}
function inflectRegularInanMissingAFem(p: string, f: string): T.Inflections {
const fBase = splitUpSyllables(f).length === 1
? accentFSylsOnNFromEnd(f, 0)
: f;
const fBase =
splitUpSyllables(f).length === 1 ? accentFSylsOnNFromEnd(f, 0) : f;
return {
fem: [
[{p, f}],
[{p: `${p}ې`, f: `${fBase}e`}],
[{p: `${p}و`, f: `${fBase}o`}],
[{ p, f }],
[{ p: `${p}ې`, f: `${fBase}e` }],
[{ p: `${p}و`, f: `${fBase}o` }],
],
};
}
@ -379,9 +438,9 @@ function inflectRegularInanEeFem(p: string, f: string): T.Inflections {
const baseF = f.slice(0, -2);
return {
fem: [
[{p, f}],
[{p: `${baseP}ۍ`, f: `${baseF}úy`}],
[{p: `${baseP}یو`, f: `${baseF}úyo`}],
[{ p, f }],
[{ p: `${baseP}ۍ`, f: `${baseF}úy` }],
[{ p: `${baseP}یو`, f: `${baseF}úyo` }],
],
};
}
@ -391,26 +450,23 @@ function inflectRegularUyFem(p: string, f: string): T.Inflections {
const baseF = removeAccents(f.slice(0, -2));
return {
fem: [
[{p, f: `${baseF}úy`}],
[{p, f: `${baseF}úy`}],
[{ p, f: `${baseF}úy` }],
[{ p, f: `${baseF}úy` }],
[
{p: `${baseP}یو`, f: `${baseF}úyo`},
{p: `${baseP}و`, f: `${baseF}o`},
{ p: `${baseP}یو`, f: `${baseF}úyo` },
{ p: `${baseP}و`, f: `${baseF}o` },
],
],
};
}
function makePashtoPlural(word: T.DictionaryEntryNoFVars): T.PluralInflections | undefined {
function makePashtoPlural(
word: T.DictionaryEntryNoFVars
): T.PluralInflections | undefined {
if (!(word.ppp && word.ppf)) return undefined;
const base = splitPsByVarients(
makePsString(word.ppp, word.ppf)
);
const base = splitPsByVarients(makePsString(word.ppp, word.ppf));
function getBaseAndO(): T.PluralInflectionSet {
return [
base,
base.flatMap(addOEnding) as T.ArrayOneOrMore<T.PsString>,
];
return [base, base.flatMap(addOEnding) as T.ArrayOneOrMore<T.PsString>];
}
if (word.c?.includes("n. m.")) {
return { masc: getBaseAndO() };
@ -422,14 +478,14 @@ function makePashtoPlural(word: T.DictionaryEntryNoFVars): T.PluralInflections |
return undefined;
}
function makeBundledPlural(word: T.DictionaryEntryNoFVars): T.PluralInflections | undefined {
function makeBundledPlural(
word: T.DictionaryEntryNoFVars
): T.PluralInflections | undefined {
if (!endsInConsonant(word) || !word.c?.includes("n.")) {
return undefined;
}
const w = makePsString(word.p, word.f);
const base = countSyllables(w) === 1
? accentOnNFromEnd(w, 0)
: w;
const base = countSyllables(w) === 1 ? accentOnNFromEnd(w, 0) : w;
return {
masc: [
[concatPsString(base, { p: "ه", f: "a" })],
@ -438,7 +494,9 @@ function makeBundledPlural(word: T.DictionaryEntryNoFVars): T.PluralInflections
};
}
function makeArabicPlural(word: T.DictionaryEntryNoFVars): T.PluralInflections | undefined {
function makeArabicPlural(
word: T.DictionaryEntryNoFVars
): T.PluralInflections | undefined {
if (!(word.apf && word.app)) return undefined;
const w = makePsString(word.app, word.apf);
const plural = splitPsByVarients(w);
@ -456,39 +514,55 @@ function makeArabicPlural(word: T.DictionaryEntryNoFVars): T.PluralInflections |
return { masc: value };
}
function makePlural(w: T.DictionaryEntryNoFVars): { plural: T.PluralInflections, bundledPlural?: T.PluralInflections } | { arabicPlural: T.PluralInflections, bundledPlural?: T.PluralInflections } | undefined {
function addSecondInf(plur: T.ArrayOneOrMore<T.PsString> | T.PsString): T.PluralInflectionSet {
function makePlural(
w: T.DictionaryEntryNoFVars
):
| { plural: T.PluralInflections; bundledPlural?: T.PluralInflections }
| { arabicPlural: T.PluralInflections; bundledPlural?: T.PluralInflections }
| undefined {
function addSecondInf(
plur: T.ArrayOneOrMore<T.PsString> | T.PsString
): T.PluralInflectionSet {
if (!Array.isArray(plur)) {
return addSecondInf([plur]);
}
return [
plur,
plur.flatMap(addOEnding) as T.ArrayOneOrMore<T.PsString>,
];
return [plur, plur.flatMap(addOEnding) as T.ArrayOneOrMore<T.PsString>];
}
if (w.c && w.c.includes("pl.")) {
const plural = addSecondInf(makePsString(w.p, w.f));
// Typescript being dumb and not letting me do a typed variable for the key
// could try refactoring with an updated TypeScript dependency
if (w.c.includes("n. m.")) return { plural: { masc: plural }};
if (w.c.includes("n. f.")) return { plural: { fem: plural }};
if (w.c.includes("n. m.")) return { plural: { masc: plural } };
if (w.c.includes("n. f.")) return { plural: { fem: plural } };
}
const arabicPlural = makeArabicPlural(w);
const pashtoPlural = makePashtoPlural(w);
const bundledPlural = makeBundledPlural(w);
function addMascPluralSuffix(animate?: boolean, shortSquish?: boolean): T.PluralInflectionSet {
function addMascPluralSuffix(
animate?: boolean,
shortSquish?: boolean
): T.PluralInflectionSet {
if (shortSquish && (w.infap === undefined || w.infaf === undefined)) {
throw new Error(`no irregular inflection info for ${w.p} - ${w.ts}`);
}
const b = removeAccents(shortSquish
? makePsString((w.infap as string).slice(0, -1), (w.infaf as string).slice(0, -1))
const b = removeAccents(
shortSquish
? makePsString(
(w.infap as string).slice(0, -1),
(w.infaf as string).slice(0, -1)
)
: w
);
const base = endsInShwa(b)
? makePsString(b.p.slice(0, -1), b.f.slice(0, -1))
: b;
return addSecondInf(
concatPsString(base, (animate && !shortSquish) ? { p: "ان", f: "áan" } : { p: "ونه", f: "óona" }),
concatPsString(
base,
animate && !shortSquish
? { p: "ان", f: "áan" }
: { p: "ونه", f: "óona" }
)
);
}
function addAnimUnisexPluralSuffix(): T.UnisexSet<T.PluralInflectionSet> {
@ -505,10 +579,14 @@ function makePlural(w: T.DictionaryEntryNoFVars): { plural: T.PluralInflections,
f: b.f.slice(0, -2),
};
const firstInf: T.ArrayOneOrMore<T.PsString> = [
concatPsString(base, { p: "یان", f: "iyáan" }, gender === "fem" ? { p: "ې", f: "e" } : ""),
...gender === "fem"
concatPsString(
base,
{ p: "یان", f: "iyáan" },
gender === "fem" ? { p: "ې", f: "e" } : ""
),
...(gender === "fem"
? [concatPsString(base, { p: "یګانې", f: "eegáane" })]
: [],
: []),
];
return [
firstInf,
@ -537,11 +615,12 @@ function makePlural(w: T.DictionaryEntryNoFVars): { plural: T.PluralInflections,
function addLongVowelSuffix(gender: "masc" | "fem"): T.PluralInflectionSet {
const base = removeEndTick(makePsString(w.p, w.f));
const baseWOutAccents = removeAccents(base);
const space = (w.p.slice(-1) === "ع" || w.p.slice(-1) === "ه") ? { p: " ", f: " " } : "";
const space =
w.p.slice(-1) === "ع" || w.p.slice(-1) === "ه" ? { p: " ", f: " " } : "";
if (gender === "fem") {
return addSecondInf([
concatPsString(base, space, { p: "وې", f: "we" }),
concatPsString(baseWOutAccents, space, { p: "ګانې", f: "gáane" })
concatPsString(baseWOutAccents, space, { p: "ګانې", f: "gáane" }),
]);
} else {
return addSecondInf([
@ -571,24 +650,32 @@ function makePlural(w: T.DictionaryEntryNoFVars): { plural: T.PluralInflections,
const shortSquish = !!w.infap && !w.infap.includes("ا");
const anim = w.c?.includes("anim.");
const type = (w.c?.includes("unisex"))
const type = w.c?.includes("unisex")
? "unisex noun"
: (w.c?.includes("n. m."))
: w.c?.includes("n. m.")
? "masc noun"
: (w.c?.includes("n. f."))
: w.c?.includes("n. f.")
? "fem noun"
: "other";
if (pashtoPlural) return {
if (pashtoPlural)
return {
plural: pashtoPlural,
arabicPlural,
};
if (type === "unisex noun") {
// doesn't need to be labelled anim - because it's only with animate nouns that you get the unisex - I THINK
if (endsInConsonant(w) && (!w.infap)) {
return { arabicPlural, bundledPlural, plural: addAnimUnisexPluralSuffix() };
if (endsInConsonant(w) && !w.infap) {
return {
arabicPlural,
bundledPlural,
plural: addAnimUnisexPluralSuffix(),
};
}
if (shortSquish && !anim) {
return { arabicPlural, plural: { masc: addMascPluralSuffix(anim, shortSquish) }};
return {
arabicPlural,
plural: { masc: addMascPluralSuffix(anim, shortSquish) },
};
}
if (endsWith([{ p: "ی", f: "éy" }, { p: "ي" }], w, true)) {
return { arabicPlural, plural: addAnimN3UnisexPluralSuffix() };
@ -598,8 +685,8 @@ function makePlural(w: T.DictionaryEntryNoFVars): { plural: T.PluralInflections,
}
if (
type === "masc noun" &&
(shortSquish || ((endsInConsonant(w) || endsInShwa(w)) && (!w.infap))) &&
(w.p.slice(-3) !== "توب")
(shortSquish || ((endsInConsonant(w) || endsInShwa(w)) && !w.infap)) &&
w.p.slice(-3) !== "توب"
) {
return {
arabicPlural,
@ -609,11 +696,7 @@ function makePlural(w: T.DictionaryEntryNoFVars): { plural: T.PluralInflections,
},
};
}
if (
type === "masc noun" &&
endsWith({ p: "ی", f: "éy" }, w, true) &&
anim
) {
if (type === "masc noun" && endsWith({ p: "ی", f: "éy" }, w, true) && anim) {
const { masc } = addAnimN3UnisexPluralSuffix();
return {
arabicPlural,
@ -630,7 +713,7 @@ function makePlural(w: T.DictionaryEntryNoFVars): { plural: T.PluralInflections,
};
}
// TODO: What about endings in long ee / animate at inanimate
if (type === "masc noun" && endsInAaOrOo(w) && (!w.infap)) {
if (type === "masc noun" && endsInAaOrOo(w) && !w.infap) {
return {
arabicPlural,
plural: {
@ -639,7 +722,7 @@ function makePlural(w: T.DictionaryEntryNoFVars): { plural: T.PluralInflections,
};
}
// TODO: What about endings in long ee / animate at inanimate
if (type === "fem noun" && endsInAaOrOo(w) && (!w.infap)) {
if (type === "fem noun" && endsInAaOrOo(w) && !w.infap) {
return {
arabicPlural,
plural: {
@ -661,6 +744,8 @@ function makePlural(w: T.DictionaryEntryNoFVars): { plural: T.PluralInflections,
return undefined;
}
export function inflectYey(ps: T.SingleOrLengthOpts<T.PsString>): T.SingleOrLengthOpts<T.UnisexInflections> {
export function inflectYey(
ps: T.SingleOrLengthOpts<T.PsString>
): T.SingleOrLengthOpts<T.UnisexInflections> {
return fmapSingleOrLengthOpts((x) => inflectRegularYeyUnisex(x.p, x.f), ps);
}

View File

@ -15,31 +15,53 @@ export function makeKid(kid: T.Kid["kid"], key?: number): T.Kid {
};
}
export function getSubjectSelection(blocks: T.EPSBlockComplete[] | T.VPSBlockComplete[]): T.SubjectSelectionComplete;
export function getSubjectSelection(blocks: T.EPSBlock[] | T.VPSBlock[]): T.SubjectSelection;
export function getSubjectSelection(blocks: T.EPSBlock[] | T.EPSBlockComplete[] | T.VPSBlockComplete[] | T.VPSBlock[]): T.SubjectSelection | T.SubjectSelectionComplete {
const b = blocks.find(f => f.block?.type === "subjectSelection");
export function getSubjectSelection(
blocks: T.EPSBlockComplete[] | T.VPSBlockComplete[]
): T.SubjectSelectionComplete;
export function getSubjectSelection(
blocks: T.EPSBlock[] | T.VPSBlock[]
): T.SubjectSelection;
export function getSubjectSelection(
blocks:
| T.EPSBlock[]
| T.EPSBlockComplete[]
| T.VPSBlockComplete[]
| T.VPSBlock[]
): T.SubjectSelection | T.SubjectSelectionComplete {
const b = blocks.find((f) => f.block?.type === "subjectSelection");
if (!b || !b.block || b.block.type !== "subjectSelection") {
throw new Error("subjectSelection not found in blocks");
}
return b.block;
}
export function getComplementFromBlocks(blocks: T.Block[][]): T.Rendered<T.ComplementSelection> | T.UnselectedComplementSelection | undefined {
const b = blocks[0].find(f => f.block.type === "complement");
return b?.block as T.Rendered<T.ComplementSelection> | T.UnselectedComplementSelection | undefined;
export function getComplementFromBlocks(
blocks: T.Block[][]
):
| T.Rendered<T.ComplementSelection>
| T.UnselectedComplementSelection
| undefined {
const b = blocks[0].find((f) => f.block.type === "complement");
return b?.block as
| T.Rendered<T.ComplementSelection>
| T.UnselectedComplementSelection
| undefined;
}
export function getSubjectSelectionFromBlocks(blocks: T.Block[][]): T.Rendered<T.SubjectSelectionComplete> {
const b = blocks[0].find(f => f.block.type === "subjectSelection");
export function getSubjectSelectionFromBlocks(
blocks: T.Block[][]
): T.Rendered<T.SubjectSelectionComplete> {
const b = blocks[0].find((f) => f.block.type === "subjectSelection");
if (!b || b.block.type !== "subjectSelection") {
throw new Error("subjectSelection not found in blocks");
}
return b.block;
}
export function getObjectSelectionFromBlocks(blocks: T.Block[][]): T.Rendered<T.ObjectSelectionComplete> {
const b = blocks[0].find(f => f.block.type === "objectSelection");
export function getObjectSelectionFromBlocks(
blocks: T.Block[][]
): T.Rendered<T.ObjectSelectionComplete> {
const b = blocks[0].find((f) => f.block.type === "objectSelection");
if (!b || b.block.type !== "objectSelection") {
throw new Error("objectSelection not found in blocks");
}
@ -48,27 +70,37 @@ export function getObjectSelectionFromBlocks(blocks: T.Block[][]): T.Rendered<T.
export function includesShrunkenServant(kids?: T.Kid[]): boolean {
if (!kids) return false;
return kids.some(k => (
k.kid.type === "mini-pronoun" && k.kid.source === "servant"
));
return kids.some(
(k) => k.kid.type === "mini-pronoun" && k.kid.source === "servant"
);
}
export function getPredicateSelectionFromBlocks(blocks: T.Block[][]): T.Rendered<T.PredicateSelectionComplete> {
const b = blocks[0].find(f => f.block.type === "predicateSelection");
export function getPredicateSelectionFromBlocks(
blocks: T.Block[][]
): T.Rendered<T.PredicateSelectionComplete> {
const b = blocks[0].find((f) => f.block.type === "predicateSelection");
if (!b || b.block.type !== "predicateSelection") {
throw new Error("predicateSelection not found in blocks");
}
return b.block;
}
export function getAPsFromBlocks(blocks: T.Block[][]): T.Rendered<T.APSelection>[] {
return blocks[0].filter(b => b.block.type === "AP").map(b => b.block) as T.Rendered<T.APSelection>[];
export function getAPsFromBlocks(
blocks: T.Block[][]
): T.Rendered<T.APSelection>[] {
return blocks[0]
.filter((b) => b.block.type === "AP")
.map((b) => b.block) as T.Rendered<T.APSelection>[];
}
export function getObjectSelection(blocks: T.VPSBlockComplete[]): T.ObjectSelectionComplete;
export function getObjectSelection(
blocks: T.VPSBlockComplete[]
): T.ObjectSelectionComplete;
export function getObjectSelection(blocks: T.VPSBlock[]): T.ObjectSelection;
export function getObjectSelection(blocks: T.VPSBlock[] | T.VPSBlockComplete[]): T.ObjectSelection | T.ObjectSelectionComplete {
const b = blocks.find(f => f.block?.type === "objectSelection");
export function getObjectSelection(
blocks: T.VPSBlock[] | T.VPSBlockComplete[]
): T.ObjectSelection | T.ObjectSelectionComplete {
const b = blocks.find((f) => f.block?.type === "objectSelection");
if (!b || !b.block || b.block.type !== "objectSelection") {
throw new Error("objectSelection not found in blocks");
}
@ -83,18 +115,24 @@ export function makeEPSBlocks(): T.EPSBlock[] {
type: "subjectSelection",
selection: undefined,
},
}
},
];
}
export function makeAPBlock(): { key: number, block: undefined } {
export function makeAPBlock(): { key: number; block: undefined } {
return {
key: Math.random(),
block: undefined,
};
}
export function makeSubjectSelection(selection: T.SubjectSelection | T.NPSelection | T.NPSelection["selection"] | undefined): T.SubjectSelection {
export function makeSubjectSelection(
selection:
| T.SubjectSelection
| T.NPSelection
| T.NPSelection["selection"]
| undefined
): T.SubjectSelection {
if (!selection) {
return {
type: "subjectSelection",
@ -115,16 +153,23 @@ export function makeSubjectSelection(selection: T.SubjectSelection | T.NPSelecti
selection: {
type: "NP",
selection,
}
},
};
}
export function makeObjectSelection(selection: T.ObjectSelection | T.ObjectNP | T.NPSelection | T.NPSelection["selection"] | undefined): T.ObjectSelection {
export function makeObjectSelection(
selection:
| T.ObjectSelection
| T.ObjectNP
| T.NPSelection
| T.NPSelection["selection"]
| undefined
): T.ObjectSelection {
if (!selection) {
return {
type: "objectSelection",
selection: undefined,
}
};
}
if (typeof selection !== "object") {
return {
@ -150,16 +195,20 @@ export function makeObjectSelection(selection: T.ObjectSelection | T.ObjectNP |
};
}
export function EPSBlocksAreComplete(blocks: T.EPSBlock[]): blocks is T.EPSBlockComplete[] {
if (blocks.some(block => block.block === undefined)) {
export function EPSBlocksAreComplete(
blocks: T.EPSBlock[]
): blocks is T.EPSBlockComplete[] {
if (blocks.some((block) => block.block === undefined)) {
return false;
}
const subject = getSubjectSelection(blocks);
return !!subject.selection;
}
export function VPSBlocksAreComplete(blocks: T.VPSBlock[]): blocks is T.VPSBlockComplete[] {
if (blocks.some(block => block.block === undefined)) {
export function VPSBlocksAreComplete(
blocks: T.VPSBlock[]
): blocks is T.VPSBlockComplete[] {
if (blocks.some((block) => block.block === undefined)) {
return false;
}
const subject = getSubjectSelection(blocks);
@ -169,70 +218,137 @@ export function VPSBlocksAreComplete(blocks: T.VPSBlock[]): blocks is T.VPSBlock
return true;
}
export function adjustSubjectSelection(blocks: T.EPSBlock[], subject: T.SubjectSelection | T.NPSelection | undefined): T.EPSBlock[];
export function adjustSubjectSelection(blocks: T.VPSBlock[], subject: T.SubjectSelection | T.NPSelection | undefined): T.VPSBlock[];
export function adjustSubjectSelection(blocks: T.VPSBlock[] | T.EPSBlock[], subject: T.SubjectSelection | T.NPSelection | undefined): T.VPSBlock[] | T.EPSBlock[] {
export function adjustSubjectSelection(
blocks: T.EPSBlock[],
subject: T.SubjectSelection | T.NPSelection | undefined
): T.EPSBlock[];
export function adjustSubjectSelection(
blocks: T.VPSBlock[],
subject: T.SubjectSelection | T.NPSelection | undefined
): T.VPSBlock[];
export function adjustSubjectSelection(
blocks: T.VPSBlock[] | T.EPSBlock[],
subject: T.SubjectSelection | T.NPSelection | undefined
): T.VPSBlock[] | T.EPSBlock[] {
const nb = [...blocks];
const i = nb.findIndex(b => b.block && b.block.type === "subjectSelection");
const i = nb.findIndex((b) => b.block && b.block.type === "subjectSelection");
if (i === -1) {
throw new Error("couldn't find subjectSelection to modify");
}
nb[i].block = subject?.type === "subjectSelection" ? subject : makeSubjectSelection(subject);
nb[i].block =
subject?.type === "subjectSelection"
? subject
: makeSubjectSelection(subject);
return nb;
}
export function adjustObjectSelection(blocks: Readonly<T.VPSBlock[]>, object: T.ObjectSelectionComplete | T.NPSelection | T.VerbObject | T.ObjectSelectionComplete): T.VPSBlockComplete[];
export function adjustObjectSelection(blocks: Readonly<T.VPSBlock[]>, object: T.ObjectSelection | T.NPSelection | T.VerbObject | T.ObjectSelection | undefined): T.EPSBlock[];
export function adjustObjectSelection(blocks: Readonly<T.VPSBlock[]>, object: T.ObjectSelection | T.ObjectSelectionComplete | T.VerbObject | T.NPSelection | undefined): T.VPSBlock[] | T.VPSBlockComplete[] {
export function adjustObjectSelection(
blocks: Readonly<T.VPSBlock[]>,
object:
| T.ObjectSelectionComplete
| T.NPSelection
| T.VerbObject
| T.ObjectSelectionComplete
): T.VPSBlockComplete[];
export function adjustObjectSelection(
blocks: Readonly<T.VPSBlock[]>,
object:
| T.ObjectSelection
| T.NPSelection
| T.VerbObject
| T.ObjectSelection
| undefined
): T.EPSBlock[];
export function adjustObjectSelection(
blocks: Readonly<T.VPSBlock[]>,
object:
| T.ObjectSelection
| T.ObjectSelectionComplete
| T.VerbObject
| T.NPSelection
| undefined
): T.VPSBlock[] | T.VPSBlockComplete[] {
const nb = [...blocks];
const i = nb.findIndex(b => b.block && b.block.type === "objectSelection");
const i = nb.findIndex((b) => b.block && b.block.type === "objectSelection");
if (i === -1) {
throw new Error("couldn't find objectSelection to modify");
}
nb[i].block = typeof object === "object" && object?.type === "objectSelection"
nb[i].block =
typeof object === "object" && object?.type === "objectSelection"
? object
: makeObjectSelection(object);
return nb;
}
export function shiftBlock<B extends T.VPSBlock[] | T.EPSBlock[]>(blocks: B, index: number, direction: "back" | "forward"): B {
const newIndex = index + (direction === "forward"
? 1 // (isNoObject(blocks[index + 1].block) ? 2 : 1)
: -1 // (isNoObject(blocks[index - 1].block) ? -2 : -2)
export function moveObjectToEnd(
blocks: T.VPSBlockComplete[]
): T.VPSBlockComplete[] {
const i = blocks.findIndex(
(b) => b.block && b.block.type === "objectSelection"
);
if (i === -1) {
throw new Error("couldn't find objectSelection to move");
}
if (i === blocks.length - 1) {
return blocks;
}
return arrayMove(blocks, i, blocks.length - 1);
}
export function shiftBlock<B extends T.VPSBlock[] | T.EPSBlock[]>(
blocks: B,
index: number,
direction: "back" | "forward"
): B {
const newIndex =
index +
(direction === "forward"
? 1 // (isNoObject(blocks[index + 1].block) ? 2 : 1)
: -1); // (isNoObject(blocks[index - 1].block) ? -2 : -2)
return arrayMove(blocks, index, newIndex) as B;
}
export function insertNewAP<B extends T.VPSBlock[] | T.EPSBlock[]>(blocks: B): B {
export function insertNewAP<B extends T.VPSBlock[] | T.EPSBlock[]>(
blocks: B
): B {
return [makeAPBlock(), ...blocks] as B;
}
export function setAP<B extends T.VPSBlock[] | T.EPSBlock[]>(blocks: B, index: number, AP: T.APSelection | undefined): B {
export function setAP<B extends T.VPSBlock[] | T.EPSBlock[]>(
blocks: B,
index: number,
AP: T.APSelection | undefined
): B {
const nBlocks = [...blocks] as B;
nBlocks[index].block = AP;
return nBlocks;
}
export function removeAP<B extends T.VPSBlock[] | T.EPSBlock[]>(blocks: B, index: number): B {
export function removeAP<B extends T.VPSBlock[] | T.EPSBlock[]>(
blocks: B,
index: number
): B {
const nBlocks = [...blocks] as B;
nBlocks.splice(index, 1);
return nBlocks;
}
export function isNoObject(b: T.VPSBlock["block"] | T.EPSBlock["block"]): b is { type: "objectSelection", selection: "none" } {
return !!(
b
&&
(b.type === "objectSelection" && b.selection === "none")
);
export function isNoObject(
b: T.VPSBlock["block"] | T.EPSBlock["block"]
): b is { type: "objectSelection"; selection: "none" } {
return !!(b && b.type === "objectSelection" && b.selection === "none");
}
export function specifyEquativeLength(blocksWVars: T.Block[][], length: "long" | "short"): T.Block[][] {
export function specifyEquativeLength(
blocksWVars: T.Block[][],
length: "long" | "short"
): T.Block[][] {
function specify(blocks: T.Block[]): T.Block[] {
const i = blocks.findIndex(b => b.block.type === "equative");
const i = blocks.findIndex((b) => b.block.type === "equative");
if (i === -1) throw new Error("equative block not found in EPRendered");
const eq = blocks[i];
if (eq.block.type !== "equative") throw new Error("error searching for equative block");
if (eq.block.type !== "equative")
throw new Error("error searching for equative block");
const adjusted = [...blocks];
adjusted[i] = {
...eq,
@ -268,23 +384,21 @@ export function isRenderedVerbB({ block }: T.Block): boolean {
if (block.type === "complement") {
return true;
}
return false
return false;
}
export function hasEquativeWithLengths(blocks: T.Block[][]): boolean {
const equative = blocks[0].find(x => x.block.type === "equative");
const equative = blocks[0].find((x) => x.block.type === "equative");
if (!equative) throw new Error("equative not found in blocks");
if (equative.block.type !== "equative") throw new Error("error finding equative in blocks");
if (equative.block.type !== "equative")
throw new Error("error finding equative in blocks");
return "long" in equative.block.equative.ps;
}
function arrayMove<X>(ar: X[], old_index: number, new_index: number): X[] {
const arr = [...ar];
const new_i = (new_index >= arr.length)
? (arr.length - 1)
: (new_index < 0)
? 0
: new_index;
const new_i =
new_index >= arr.length ? arr.length - 1 : new_index < 0 ? 0 : new_index;
arr.splice(new_i, 0, arr.splice(old_index, 1)[0]);
return arr;
};
}

View File

@ -22,6 +22,8 @@ import {
specifyEquativeLength,
} from "./blocks-utils";
import { blank, kidsBlank } from "../misc-helpers";
import { monoidPsStringWVars } from "../fp-ps";
import { concatAll } from "fp-ts/lib/Monoid";
type BlankoutOptions = {
equative?: boolean;
@ -215,64 +217,66 @@ export function combineIntoText(
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),
};
return piecesWVars
.map((pieces) => {
const psVarsBlocks = getPsVarsBlocks(
applyBlankOut(pieces, blankOut),
subjectPerson
);
return concatAll(monoidPsStringWVars)(psVarsBlocks);
})
.flat();
}
function getPsVarsBlocks(
pieces: (T.Block | T.Kid | T.PsString)[],
subjectPerson: T.Person
): T.PsString[][] {
const space = [{ p: " ", f: " " }];
const phToCliticNegSpace = [{ p: "", f: "-" }];
const endIndex = pieces.length - 1;
return pieces.reduce<T.PsString[][]>((acc, x, i) => {
const next = pieces[i + 1];
if ("p" in x) {
return [...acc, [x]];
}
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)) ||
const ps = getPsFromPiece(x, subjectPerson);
return [
...acc,
ps,
...(i === endIndex
? []
: "block" in x && x.block.type === "PH"
? "kid" in next || ("block" in next && next.block.type === "negative")
? [phToCliticNegSpace]
: []
: [space]),
];
}, []);
}
function applyBlankOut(
pieces: (T.Block | T.Kid | T.PsString)[],
blankOut: BlankoutOptions | undefined
): (T.Block | T.Kid | T.PsString)[] {
if (!blankOut) return pieces;
return pieces.map((x) => {
if (
(blankOut.equative && "block" in x && x.block.type === "equative") ||
(blankOut.verb && "block" in x && isRenderedVerbB(x)) ||
(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;
"block" in x &&
x.block.type === "predicateSelection")
) {
return blank;
}
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 (blankOut?.ba && "kid" in x && x.kid.type === "ba") {
return kidsBlank;
}
return piecesWVars.flatMap(combine);
if (blankOut?.negative && "block" in x && x.block.type === "negative") {
return { p: "", f: "" };
}
return x;
});
}
function getPsFromPiece(

View File

@ -12,14 +12,18 @@ export function makeAdverbSelection(entry: T.AdverbEntry): T.AdverbSelection {
};
}
export function makeLocativeAdverbSelection(entry: T.LocativeAdverbEntry): T.LocativeAdverbSelection {
export function makeLocativeAdverbSelection(
entry: T.LocativeAdverbEntry
): T.LocativeAdverbSelection {
return {
type: "loc. adv.",
entry: entry,
};
}
export function makeAdjectiveSelection(entry: T.AdjectiveEntry): T.AdjectiveSelection {
export function makeAdjectiveSelection(
entry: T.AdjectiveEntry
): T.AdjectiveSelection {
return {
type: "adjective",
entry: entry,
@ -27,7 +31,9 @@ export function makeAdjectiveSelection(entry: T.AdjectiveEntry): T.AdjectiveSele
};
}
export function makeParticipleSelection(verb: T.VerbEntry): T.ParticipleSelection {
export function makeParticipleSelection(
verb: T.VerbEntry
): T.ParticipleSelection {
return {
type: "participle",
verb,
@ -35,7 +41,11 @@ export function makeParticipleSelection(verb: T.VerbEntry): T.ParticipleSelectio
};
}
export function makeNounSelection(entry: T.NounEntry, old: T.NounSelection | undefined, dynamicComplement?: true): T.NounSelection {
export function makeNounSelection(
entry: T.NounEntry,
old: T.NounSelection | undefined,
complementType?: "dynamic" | "generative stative"
): T.NounSelection {
const number = isPluralNounEntry(entry) ? "plural" : "singular";
return {
type: "noun",
@ -44,9 +54,10 @@ export function makeNounSelection(entry: T.NounEntry, old: T.NounSelection | und
genderCanChange: isUnisexNounEntry(entry),
number,
numberCanChange: number === "singular",
adjectives: (!dynamicComplement && old) ? old.adjectives : [],
possesor: !dynamicComplement ? old?.possesor : undefined,
dynamicComplement,
adjectives: !complementType && old ? old.adjectives : [],
possesor: !complementType ? old?.possesor : undefined,
dynamicComplement: complementType === "dynamic",
genStativeComplement: complementType === "generative stative",
demonstrative: undefined,
};
}

View File

@ -1,5 +1,5 @@
import * as T from "../../../types";
import { mapVerbRenderedOutput } from "../fmaps";
import { mapVerbRenderedOutput } from "../fp-ps";
import { removeAccents } from "../accent-helpers";
import { getPersonFromNP, isPastTense } from "./vp-tools";
import { isImperativeTense, isPattern4Entry } from "../type-predicates";
@ -19,6 +19,7 @@ import {
getMiniPronounPs,
} from "./render-common";
import { renderComplementSelection } from "./render-complement";
import { statVerb } from "../new-verb-engine/roots-and-stems";
export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
const subject = getSubjectSelection(VP.blocks).selection;
@ -45,8 +46,18 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
king,
complementPerson,
});
// TODO: for dynamic -
const { vbs, hasBa } = renderVerb({
verb: VP.verb.verb,
verb:
VP.verb.isCompound === "generative stative"
? statVerb[
VP.verb.transitivity === "intransitive"
? "intransitive"
: "transitive"
]
: VP.verb.dynAuxVerb
? VP.verb.dynAuxVerb
: VP.verb.verb,
tense: VP.verb.tense,
subject: subjectPerson,
object: objectPerson,
@ -128,7 +139,6 @@ export function insertNegative(
if (!negative) {
return [blocks.flat().map(makeBlock)];
}
const blocksA = blocks.flat().map(makeBlock);
const blocksNoAccentA = mapVerbRenderedOutput(removeAccents, blocks)
.flat()
.map(makeBlock);
@ -138,13 +148,13 @@ export function insertNegative(
// 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(swapEndingBlocks(blocksNoAccentA), neg, 2),
insertFromEnd(swapEndingBlocks(blocksNoAccentA, 2), neg, 3),
insertFromEnd(blocksNoAccentA, neg, 1),
];
}
return [
insertFromEnd(swapEndingBlocks(blocksA), neg, 2),
insertFromEnd(swapEndingBlocks(blocksNoAccentA), neg, 2),
insertFromEnd(blocksNoAccentA, neg, 1),
];
}
@ -280,8 +290,9 @@ function whatsAdjustable(
VP: T.VPSelectionComplete
): "both" | "king" | "servant" {
// TODO: intransitive dynamic compounds?
return VP.verb.isCompound === "dynamic" &&
VP.verb.transitivity === "transitive"
return VP.verb.isCompound === "dynamic" ||
(VP.verb.isCompound === "generative stative" &&
VP.verb.transitivity === "transitive")
? isPastTense(VP.verb.tense)
? "servant"
: "king"

View File

@ -1,6 +1,4 @@
import {
makeNounSelection,
} from "./make-selections";
import { makeNounSelection } from "./make-selections";
import * as T from "../../../types";
import { getVerbInfo } from "../verb-info";
import {
@ -9,16 +7,24 @@ import {
getSubjectSelection,
makeObjectSelection,
makeSubjectSelection,
moveObjectToEnd,
} from "./blocks-utils";
export function makeVPSelectionState(
verb: T.VerbEntry,
os?: T.VPSelectionState,
os?: T.VPSelectionState
): T.VPSelectionState {
const info = getVerbInfo(verb.entry, verb.complement);
const subject = (os?.verb.voice === "passive" && info.type === "dynamic compound")
? makeNounSelection(info.objComplement.entry as T.NounEntry, undefined, true)
: (os?.blocks ? getSubjectSelection(os.blocks).selection : undefined);
const subject =
os?.verb.voice === "passive" && info.type === "dynamic compound"
? makeNounSelection(
info.objComplement.entry as T.NounEntry,
undefined,
"dynamic"
)
: os?.blocks
? getSubjectSelection(os.blocks).selection
: undefined;
function getTransObjFromos() {
const osObj = os ? getObjectSelection(os.blocks).selection : undefined;
if (
@ -26,32 +32,52 @@ export function makeVPSelectionState(
osObj === "none" ||
typeof osObj === "number" ||
os.verb.isCompound === "dynamic" ||
(osObj?.selection.type === "noun" && osObj.selection.dynamicComplement)
) return undefined;
(osObj?.selection.type === "noun" &&
(osObj.selection.dynamicComplement ||
osObj.selection.genStativeComplement))
)
return undefined;
return osObj;
}
const transitivity: T.Transitivity = "grammaticallyTransitive" in info
? "transitive"
: info.transitivity;
const object = (transitivity === "grammatically transitive")
const transitivity: T.Transitivity =
"grammaticallyTransitive" in info ? "transitive" : info.transitivity;
const object =
transitivity === "grammatically transitive"
? T.Person.ThirdPlurMale
: (info.type === "dynamic compound" && os?.verb.voice !== "passive")
? makeNounSelection(info.objComplement.entry as T.NounEntry, undefined, true)
: (transitivity === "transitive" && os?.verb.voice !== "passive")
: (info.type === "dynamic compound" ||
info.type === "generative stative compound") &&
os?.verb.voice !== "passive"
? makeNounSelection(
info.objComplement.entry as T.NounEntry,
undefined,
info.type === "dynamic compound" ? "dynamic" : "generative stative"
)
: info.type === "dynamic or generative stative compound" &&
os?.verb.voice !== "passive"
? makeNounSelection(
info.dynamic.objComplement.entry as T.NounEntry,
undefined,
"generative stative"
)
: transitivity === "transitive" && os?.verb.voice !== "passive"
? getTransObjFromos()
: "none";
const isCompound = ("stative" in info || info.type === "stative compound")
const isCompound =
"stative" in info && info.type === "dynamic or generative stative compound"
? "generative stative"
: "stative" in info || info.type === "stative compound"
? "stative"
: info.type === "dynamic compound"
? "dynamic"
: false;
// TODO: here and below in the changeStatDyn function ... allow for entries with complement
const dynAuxVerb: T.VerbEntry | undefined = isCompound !== "dynamic"
const dynAuxVerb: T.VerbEntry | undefined =
isCompound !== "dynamic"
? undefined
: info.type === "dynamic compound"
? { entry: info.auxVerb } as T.VerbEntry
? ({ entry: info.auxVerb } as T.VerbEntry)
: "dynamic" in info
? { entry: info.dynamic.auxVerb } as T.VerbEntry
? ({ entry: info.dynamic.auxVerb } as T.VerbEntry)
: undefined;
const blocks = [
{ key: Math.random(), block: makeSubjectSelection(subject) },
@ -69,18 +95,18 @@ export function makeVPSelectionState(
tenseCategory: os ? os.verb.tenseCategory : "basic",
transitivity,
isCompound,
voice: transitivity === "transitive"
? (os?.verb.voice || "active")
: "active",
voice:
transitivity === "transitive" ? os?.verb.voice || "active" : "active",
negative: os ? os.verb.negative : false,
canChangeTransitivity: "grammaticallyTransitive" in info,
canChangeVoice: transitivity === "transitive",
canChangeStatDyn: "stative" in info,
},
externalComplement: takesExternalComplement(verb)
? { type: "complement", selection: { type: "unselected" }}
? { type: "complement", selection: { type: "unselected" } }
: undefined,
form: (os && info.type !== "dynamic compound")
form:
os && info.type !== "dynamic compound"
? os.form
: { removeKing: false, shrinkServant: false },
};
@ -96,33 +122,62 @@ function takesExternalComplement(v: T.VerbEntry): boolean {
return false;
}
export function changeStatDyn(v: T.VPSelectionState, s: "dynamic" | "stative"): T.VPSelectionState {
export function changeStatDyn(
v: T.VPSelectionState,
s: "dynamic" | "stative"
): T.VPSelectionState {
const info = getVerbInfo(v.verb.verb.entry, v.verb.verb.complement);
if (!("stative" in info)) {
return v;
}
const newBlocks = adjustObjectSelection(
v.blocks,
s === "dynamic" ||
(s === "stative" &&
info.type === "dynamic or generative stative compound")
? {
type: "NP",
selection: makeNounSelection(
info.dynamic.objComplement.entry as T.NounEntry,
undefined,
s === "dynamic" ? "dynamic" : "generative stative"
),
}
: undefined
);
return {
...v,
blocks: adjustObjectSelection(
v.blocks,
s === "dynamic"
? { type: "NP", selection: makeNounSelection(info.dynamic.objComplement.entry as T.NounEntry, undefined, true) }
: undefined,
),
blocks:
s === "stative" && info.type === "dynamic or generative stative compound"
? moveObjectToEnd(newBlocks)
: newBlocks,
verb: {
...v.verb,
isCompound: s,
dynAuxVerb: s === "dynamic"
? { entry: info.dynamic.auxVerb } as T.VerbEntry
isCompound:
info.type === "dynamic or generative stative compound" &&
s === "stative"
? "generative stative"
: s,
dynAuxVerb:
s === "dynamic"
? ({ entry: info.dynamic.auxVerb } as T.VerbEntry)
: undefined,
},
};
}
export function changeTransitivity(v: T.VPSelectionState, transitivity: "transitive" | "grammatically transitive"): T.VPSelectionState {
export function changeTransitivity(
v: T.VPSelectionState,
transitivity: "transitive" | "grammatically transitive"
): T.VPSelectionState {
return {
...v,
blocks: adjustObjectSelection(v.blocks, transitivity === "grammatically transitive" ? T.Person.ThirdPlurMale : undefined),
blocks: adjustObjectSelection(
v.blocks,
transitivity === "grammatically transitive"
? T.Person.ThirdPlurMale
: undefined
),
verb: {
...v.verb,
transitivity,

View File

@ -132,12 +132,14 @@ export function isInvalidSubjObjCombo(subj: T.Person, obj: T.Person): boolean {
// throw new Error("unknown tense");
// }
export function getPersonFromNP(np: T.NPSelection): T.Person;
export function getPersonFromNP(
np: T.NPSelection | T.ObjectNP
np: T.NPSelection | T.Rendered<T.NPSelection>
): T.Person;
export function getPersonFromNP(
np: T.NPSelection | T.Rendered<T.NPSelection> | T.ObjectNP
): T.Person | undefined;
export function getPersonFromNP(
np: T.NPSelection | T.ObjectNP
np: T.NPSelection | T.Rendered<T.NPSelection> | T.ObjectNP
): T.Person | undefined {
if (np === "none") {
return undefined;

View File

@ -28,15 +28,9 @@ import {
psStringEquals,
} from "./p-text-helpers";
import { makePsString } from "./accent-and-ps-utils";
import {
inflectYey,
} from "./pashto-inflector";
import {
accentOnNFromEnd, removeAccents,
} from "./accent-helpers";
import {
mapInflections,
} from "./fmaps";
import { inflectYey } from "./pashto-inflector";
import { accentOnNFromEnd, removeAccents } from "./accent-helpers";
import { mapInflections } from "./fp-ps";
import { pashtoConsonants } from "./pashto-consonants";
import {
checkForIrregularConjugation,
@ -49,14 +43,29 @@ import {
} from "./misc-helpers";
import * as T from "../../types";
const dummyEntry: T.DictionaryEntry = { i: 0, p: "", f: "", g: "", e: "", c: "", ts: 0 };
const dummyEntry: T.DictionaryEntry = {
i: 0,
p: "",
f: "",
g: "",
e: "",
c: "",
ts: 0,
};
const aayTail = [{p: "ای", f: "aay" }, { p: "ی", f: "ey" }];
const aayTail = [
{ p: "ای", f: "aay" },
{ p: "ی", f: "ey" },
];
export function conjugateVerb(entry: T.DictionaryEntry, complement?: T.DictionaryEntry, verbInfo?: T.NonComboVerbInfo): T.VerbOutput {
export function conjugateVerb(
entry: T.DictionaryEntry,
complement?: T.DictionaryEntry,
verbInfo?: T.NonComboVerbInfo
): T.VerbOutput {
if (!(entry.c && entry.c.slice(0, 2) === "v.")) {
throw new Error("not a verb");
};
}
const irregularConj = checkForIrregularConjugation(entry);
if (irregularConj) {
return irregularConj;
@ -65,16 +74,35 @@ export function conjugateVerb(entry: T.DictionaryEntry, complement?: T.Dictionar
if (info.type === "transitive or grammatically transitive simple") {
return {
info,
transitive: conjugateVerb({ ...entry, c: entry.c ? entry.c.replace("/gramm. trans.", "") : "" }, dummyEntry, info.transitive) as T.VerbConjugation,
grammaticallyTransitive: conjugateVerb({ ...entry, c: entry.c ? entry.c?.replace("trans./", "") : "" }, dummyEntry, info.grammaticallyTransitive) as T.VerbConjugation,
transitive: conjugateVerb(
{ ...entry, c: entry.c ? entry.c.replace("/gramm. trans.", "") : "" },
dummyEntry,
info.transitive
) as T.VerbConjugation,
grammaticallyTransitive: conjugateVerb(
{ ...entry, c: entry.c ? entry.c?.replace("trans./", "") : "" },
dummyEntry,
info.grammaticallyTransitive
) as T.VerbConjugation,
};
}
if (info.type === "dynamic or stative compound" || info.type === "dynamic or generative stative compound") {
if (
info.type === "dynamic or stative compound" ||
info.type === "dynamic or generative stative compound"
) {
return {
info,
stative: conjugateVerb({ ...entry, c: entry.c ? entry.c.replace("dyn./", "") : "" }, dummyEntry, info.stative) as T.VerbConjugation,
dynamic: conjugateVerb({ ...entry, c: entry.c ? entry.c.replace("/stat.", "") : "" }, dummyEntry, info.dynamic) as T.VerbConjugation,
stative: conjugateVerb(
{ ...entry, c: entry.c ? entry.c.replace("dyn./", "") : "" },
dummyEntry,
info.stative
) as T.VerbConjugation,
dynamic: conjugateVerb(
{ ...entry, c: entry.c ? entry.c.replace("/stat.", "") : "" },
dummyEntry,
info.dynamic
) as T.VerbConjugation,
};
}
@ -91,14 +119,22 @@ export function conjugateVerb(entry: T.DictionaryEntry, complement?: T.Dictionar
hypothetical: makeHypotheticalContent(nonComboInfo),
participle: makeParticipleContent(nonComboInfo),
perfect: makePerfectContent(nonComboInfo),
..."singularForm" in info ? {
singularForm: conjugateVerb(entry, complement, info.singularForm) as T.VerbConjugation,
} : {},
...("singularForm" in info
? {
singularForm: conjugateVerb(
entry,
complement,
info.singularForm
) as T.VerbConjugation,
}
: {}),
// if transitive include passive voice
...info.transitivity !== "intransitive" ? {
...(info.transitivity !== "intransitive"
? {
// TODO: STATIVE COMPOUND VERSION OF THIS
passive: makePassiveContent(nonComboInfo),
} : {},
}
: {}),
};
return nonComboInfo.transitivity === "grammatically transitive"
@ -108,7 +144,9 @@ export function conjugateVerb(entry: T.DictionaryEntry, complement?: T.Dictionar
: conjugation;
}
function conjugateDynamicCompound(info: T.DynamicCompoundVerbInfo): T.VerbConjugation {
function conjugateDynamicCompound(
info: T.DynamicCompoundVerbInfo
): T.VerbConjugation {
// const willUseImperative = !(
// info.type === "dynamic compound"
// && info.transitivity === "intransitive"
@ -116,18 +154,24 @@ function conjugateDynamicCompound(info: T.DynamicCompoundVerbInfo): T.VerbConjug
// );
const auxConj = enforceObject(
conjugateVerb(info.auxVerb, info.auxVerbComplement) as T.VerbConjugation,
info.objComplement.person,
info.objComplement.person
);
const complement = info.objComplement.plural
? info.objComplement.plural
: makePsString(info.objComplement.entry.p, info.objComplement.entry.f);
const makeAspectContent = (aspect: T.Aspect): T.AspectContent => {
const makeDynamicModalContent = (): T.ModalContent => {
const nonImperative = addToForm([complement, " "], auxConj[aspect].modal.nonImperative);
const nonImperative = addToForm(
[complement, " "],
auxConj[aspect].modal.nonImperative
);
const future = addToForm([baParticle, " "], nonImperative);
const past = addToForm([complement, " "], auxConj[aspect].modal.past);
const habitualPast = addToForm([baParticle, " "], past);
const hypotheticalPast = addToForm([complement, " "], auxConj[aspect].modal.hypotheticalPast);
const hypotheticalPast = addToForm(
[complement, " "],
auxConj[aspect].modal.hypotheticalPast
);
return {
nonImperative,
future,
@ -151,20 +195,18 @@ function conjugateDynamicCompound(info: T.DynamicCompoundVerbInfo): T.VerbConjug
habitualPast,
modal,
};
}
};
const hypothetical = addToForm([complement, " "], auxConj.hypothetical);
const auxPPart = auxConj.participle.past;
const participle = {
present: concatInflections(complement, auxConj.participle.present),
past: (
(("long" in auxPPart) && ("masc" in auxPPart.long)) ||
("masc" in auxPPart)
)
// @ts-ignore
? concatInflections(complement, auxPPart)
// @ts-ignore
: concatPsString(complement, " ", auxPPart)
}
past:
("long" in auxPPart && "masc" in auxPPart.long) || "masc" in auxPPart
? // @ts-ignore
concatInflections(complement, auxPPart)
: // @ts-ignore
concatPsString(complement, " ", auxPPart),
};
const makePerfect = (pset: T.PerfectContent): T.PerfectContent => ({
halfPerfect: addToForm([complement, " "], pset.halfPerfect),
past: addToForm([complement, " "], pset.past),
@ -176,12 +218,21 @@ function conjugateDynamicCompound(info: T.DynamicCompoundVerbInfo): T.VerbConjug
pastSubjunctive: addToForm([complement, " "], pset.pastSubjunctive),
wouldHaveBeen: addToForm([complement, " "], pset.wouldHaveBeen),
});
const makePassiveAspectContent = (aspect: T.Aspect, passive: T.PassiveContent): T.AspectContentPassive => {
const nonImperative = addToForm([complement, " "], passive[aspect].nonImperative);
const makePassiveAspectContent = (
aspect: T.Aspect,
passive: T.PassiveContent
): T.AspectContentPassive => {
const nonImperative = addToForm(
[complement, " "],
passive[aspect].nonImperative
);
const future = addToForm([baParticle, " "], nonImperative);
const past = addToForm([complement, " "], passive[aspect].past);
const habitualPast = addToForm([baParticle, " "], past);
const modal = makePassiveModalSection([complement, " "], stativeAux.intransitive.imperfective.modal);
const modal = makePassiveModalSection(
[complement, " "],
stativeAux.intransitive.imperfective.modal
);
return {
imperative: undefined,
nonImperative,
@ -189,8 +240,8 @@ function conjugateDynamicCompound(info: T.DynamicCompoundVerbInfo): T.VerbConjug
past,
habitualPast,
modal,
}
}
};
};
return {
info,
imperfective: makeAspectContent("imperfective"),
@ -198,24 +249,36 @@ function conjugateDynamicCompound(info: T.DynamicCompoundVerbInfo): T.VerbConjug
hypothetical,
participle,
perfect: makePerfect(auxConj.perfect),
...auxConj.passive ? {
...(auxConj.passive
? {
passive: {
imperfective: makePassiveAspectContent("imperfective", auxConj.passive),
imperfective: makePassiveAspectContent(
"imperfective",
auxConj.passive
),
perfective: makePassiveAspectContent("perfective", auxConj.passive),
perfect: makePerfect(auxConj.passive.perfect),
},
} : {},
...info.singularForm ? {
singularForm: conjugateDynamicCompound(info.singularForm)
} : {},
...info.intransitiveForm ? {
intransitiveForm: conjugateDynamicCompound(info.intransitiveForm)
} : {},
}
: {}),
...(info.singularForm
? {
singularForm: conjugateDynamicCompound(info.singularForm),
}
: {}),
...(info.intransitiveForm
? {
intransitiveForm: conjugateDynamicCompound(info.intransitiveForm),
}
: {}),
};
}
function makeAspectContent(info: T.NonComboVerbInfo, aspect: T.Aspect): T.AspectContent {
if ((info.type === "stative compound") && spaceInForm(info.root[aspect])) {
function makeAspectContent(
info: T.NonComboVerbInfo,
aspect: T.Aspect
): T.AspectContent {
if (info.type === "stative compound" && spaceInForm(info.root[aspect])) {
return makeStativeCompoundSeperatedAspectContent(info, aspect);
}
const stem = noPersInfs(info.stem[aspect]);
@ -223,7 +286,10 @@ function makeAspectContent(info: T.NonComboVerbInfo, aspect: T.Aspect): T.Aspect
const nonImperative = addToForm([stem], presentEndings);
const future = addToForm([baParticle, " "], nonImperative);
const imperative = addToForm([stem], imperativeEndings);
const roughPast = addToForm([root], pastEndings) as T.LengthOptions<T.VerbBlock>;
const roughPast = addToForm(
[root],
pastEndings
) as T.LengthOptions<T.VerbBlock>;
// add accents and idiosyncratic third person sing masc forms
const past = finishSimpleVerbPast(info, aspect, roughPast);
const habitualPast = addToForm([baParticle, " "], past);
@ -237,14 +303,26 @@ function makeAspectContent(info: T.NonComboVerbInfo, aspect: T.Aspect): T.Aspect
};
}
function makeJoinedModalContent(info: T.NonComboVerbInfo, aspectIn: T.Aspect): T.ModalContent {
function makeJoinedModalContent(
info: T.NonComboVerbInfo,
aspectIn: T.Aspect
): T.ModalContent {
const aspect: T.Aspect = noPerfectiveModal(info) ? "imperfective" : aspectIn;
const aux = stativeAux.intransitive.perfective;
const rAndT = info.yulEnding
? [concatPsString(noPersInfs(info.root[aspect]).long, aayTail[1]), concatPsString(noPersInfs(info.root[aspect]).long, aayTail[0])]
: [concatPsString(noPersInfs(info.root[aspect]), aayTail[1]), concatPsString(noPersInfs(info.root[aspect]), aayTail[0])]
const rootAndTail = aspect === "imperfective"
? rAndT.map((x: T.PsString | T.LengthOptions<T.PsString>) => accentImperfectiveModalRootAndTail(info, x))
? [
concatPsString(noPersInfs(info.root[aspect]).long, aayTail[1]),
concatPsString(noPersInfs(info.root[aspect]).long, aayTail[0]),
]
: [
concatPsString(noPersInfs(info.root[aspect]), aayTail[1]),
concatPsString(noPersInfs(info.root[aspect]), aayTail[0]),
];
const rootAndTail =
aspect === "imperfective"
? rAndT.map((x: T.PsString | T.LengthOptions<T.PsString>) =>
accentImperfectiveModalRootAndTail(info, x)
)
: rAndT;
const nonImperative = addToForm([rootAndTail, " "], aux.nonImperative);
@ -252,7 +330,7 @@ function makeJoinedModalContent(info: T.NonComboVerbInfo, aspectIn: T.Aspect): T
const past = addToForm(
[rootAndTail, " "],
// @ts-ignore
aux.past.short,
aux.past.short
);
const habitualPast = addToForm([baParticle, " "], past);
function mhp(rt: T.PsString[]): T.VerbBlock {
@ -270,11 +348,13 @@ function makeJoinedModalContent(info: T.NonComboVerbInfo, aspectIn: T.Aspect): T
[form, form],
];
}
const hypotheticalPast = "short" in rootAndTail[0]
const hypotheticalPast =
"short" in rootAndTail[0]
? {
short: mhp(rootAndTail.map((rt) => "short" in rt ? rt.short : rt)),
long: mhp(rootAndTail.map((rt) => "short" in rt ? rt.long : rt)),
} : mhp(rootAndTail.map((rt) => "short" in rt ? rt.long : rt));
short: mhp(rootAndTail.map((rt) => ("short" in rt ? rt.short : rt))),
long: mhp(rootAndTail.map((rt) => ("short" in rt ? rt.long : rt))),
}
: mhp(rootAndTail.map((rt) => ("short" in rt ? rt.long : rt)));
// const hypotheticalPast = [
// [concatPsString(rootAndTail[0], " ", { p: "سو", f: "shw" }, aayTail[0])]
@ -289,18 +369,26 @@ function makeJoinedModalContent(info: T.NonComboVerbInfo, aspectIn: T.Aspect): T
};
}
function makeStativeCompoundSeperatedAspectContent(info: T.StativeCompoundVerbInfo, aspect: T.Aspect): T.AspectContent {
function makeStativeCompoundSeperatedAspectContent(
info: T.StativeCompoundVerbInfo,
aspect: T.Aspect
): T.AspectContent {
const transitivity = getTransitivity(info);
const complement: T.UnisexInflections = aspect === "imperfective"
const complement: T.UnisexInflections =
aspect === "imperfective"
? mapInflections(removeAccents, info.complement)
: info.complement;
const presentComplement = (transitivity === "transitive" && complementInflects(complement))
const presentComplement =
transitivity === "transitive" && complementInflects(complement)
? unisexInfToObjectMatrix(complement) // transitive verb requires an object matrix for the complex
: complement; // intransitive verb doesn't require that because the complement matches the subject
function makeTransitiveStativeModalContent() {
const aux = stativeAux[transitivity][aspect].modal;
const nonImperative = addToForm([presentComplement, " "], aux.nonImperative);
const nonImperative = addToForm(
[presentComplement, " "],
aux.nonImperative
);
const future = addToForm([baParticle, " "], nonImperative);
const past = addToForm([complement, " "], aux.past);
const habitualPast = addToForm([baParticle, " "], past);
@ -318,7 +406,7 @@ function makeStativeCompoundSeperatedAspectContent(info: T.StativeCompoundVerbIn
// CHECK, does this work with transitive and intransitive??
const nonImperative = addToForm(
[presentComplement, " "],
stativeAux[transitivity][aspect].nonImperative,
stativeAux[transitivity][aspect].nonImperative
);
const future = addToForm([baParticle, " "], nonImperative);
const imperative = addToForm([presentComplement, " "], aux.imperative);
@ -330,27 +418,38 @@ function makeStativeCompoundSeperatedAspectContent(info: T.StativeCompoundVerbIn
past,
habitualPast,
imperative,
modal: info.transitivity === "transitive"
modal:
info.transitivity === "transitive"
? makeTransitiveStativeModalContent()
: makeJoinedModalContent(info, "imperfective"),
};
}
function makeHypotheticalContent(info: T.NonComboVerbInfo): T.VerbForm {
function makeStativeCompoundSepHypotheticalContent(info: T.StativeCompoundVerbInfo): T.VerbForm {
function makeStativeCompoundSepHypotheticalContent(
info: T.StativeCompoundVerbInfo
): T.VerbForm {
const transitivity = getTransitivity(info);
const aux = stativeAux[transitivity].hypothetical;
return addToForm([
(transitivity === "transitive" && complementInflects(info.complement))
return addToForm(
[
transitivity === "transitive" && complementInflects(info.complement)
? unisexInfToObjectMatrix(info.complement)
: info.complement,
" ",
], aux);
],
aux
);
}
if (("complement" in info) && spaceInForm(info.root.imperfective)) {
return makeStativeCompoundSepHypotheticalContent(info as T.StativeCompoundVerbInfo);
if ("complement" in info && spaceInForm(info.root.imperfective)) {
return makeStativeCompoundSepHypotheticalContent(
info as T.StativeCompoundVerbInfo
);
}
const makeHypothetical = (root: T.OptionalPersonInflections<T.LengthOptions<T.PsString>>, length: "short" | "long"): T.PsString[] => {
const makeHypothetical = (
root: T.OptionalPersonInflections<T.LengthOptions<T.PsString>>,
length: "short" | "long"
): T.PsString[] => {
if ("mascSing" in root) {
// BIG TODO: SHOULD THERE BE PERS INFS HERE?? IGNORING THEM NOW IF THEY EXIST
return makeHypothetical(root.mascSing, length) as T.PsString[];
@ -358,16 +457,22 @@ function makeHypotheticalContent(info: T.NonComboVerbInfo): T.VerbForm {
return [
accentOnNFromEnd(
concatPsString(root[length], aayTail[0]),
(length === "long" ? 1 : 0) + (info.yulEnding ? 1 : 0),
(length === "long" ? 1 : 0) + (info.yulEnding ? 1 : 0)
),
accentOnNFromEnd(
concatPsString(root[length], aayTail[1]),
(length === "long" ? 1 : 0) + (info.yulEnding ? 1 : 0),
(length === "long" ? 1 : 0) + (info.yulEnding ? 1 : 0)
),
];
};
const short = makeHypothetical(info.root.imperfective, "short") as T.ArrayOneOrMore<T.PsString>;
const long = makeHypothetical(info.root.imperfective, "long") as T.ArrayOneOrMore<T.PsString>;
const short = makeHypothetical(
info.root.imperfective,
"short"
) as T.ArrayOneOrMore<T.PsString>;
const long = makeHypothetical(
info.root.imperfective,
"long"
) as T.ArrayOneOrMore<T.PsString>;
return {
short: [
[short, short],
@ -390,13 +495,26 @@ function makeHypotheticalContent(info: T.NonComboVerbInfo): T.VerbForm {
function makeParticipleContent(info: T.NonComboVerbInfo): T.ParticipleContent {
const transitivity = getTransitivity(info);
const past = ("complement" in info)
? concatInflections(info.complement, stativeAux[transitivity].participle.past as T.UnisexInflections)
: ("objComplement" in info)
? concatInflections(info.objComplement.plural ? info.objComplement.plural : info.objComplement.entry, stativeAux[transitivity].participle.past as T.UnisexInflections)
const past =
"complement" in info
? concatInflections(
info.complement,
stativeAux[transitivity].participle.past as T.UnisexInflections
)
: "objComplement" in info
? concatInflections(
info.objComplement.plural
? info.objComplement.plural
: info.objComplement.entry,
stativeAux[transitivity].participle.past as T.UnisexInflections
)
: inflectYey(noPersInfs(info.participle.past));
const present = ("complement" in info && spaceInForm(info.root.imperfective))
? concatInflections(info.complement, stativeAux[transitivity].participle.present as T.UnisexInflections)
const present =
"complement" in info && spaceInForm(info.root.imperfective)
? concatInflections(
info.complement,
stativeAux[transitivity].participle.present as T.UnisexInflections
)
: inflectYey(noPersInfs(info.participle.present));
return {
present, // PRESENT PARTICIPLE inflected
@ -406,22 +524,45 @@ function makeParticipleContent(info: T.NonComboVerbInfo): T.ParticipleContent {
function makePerfectContent(info: T.NonComboVerbInfo): T.PerfectContent {
const transitivity = getTransitivity(info);
const pastPart: (" " | T.SingleOrLengthOpts<T.UnisexInflections> | T.SingleOrLengthOpts<T.PsString>)[] =
(info.type === "stative compound")
// for stative compounds
? [mapInflections(removeAccents, info.complement), " ", stativeAux[transitivity].participle.past]
// for regular compounds
: [inflectYey(noPersInfs(info.participle.past))]
const pastPart: (
| " "
| T.SingleOrLengthOpts<T.UnisexInflections>
| T.SingleOrLengthOpts<T.PsString>
)[] =
info.type === "stative compound"
? // for stative compounds
[
mapInflections(removeAccents, info.complement),
" ",
stativeAux[transitivity].participle.past,
]
: // for regular compounds
[inflectYey(noPersInfs(info.participle.past))];
const halfPerfect = addToForm([...pastPart], emptyVerbBlock);
const past = addToForm([...pastPart, " "], equativeEndings.past.short);
const present = addToForm([...pastPart, " "], equativeEndings.present);
const habitual = addToForm([...pastPart, " "], equativeEndings.habitual);
const subjunctive = addToForm([...pastPart, " "], equativeEndings.subjunctive);
const future = addToForm([baParticle, " ", ...pastPart, " "], equativeEndings.habitual);
const wouldBe = addToForm([baParticle, " ", ...pastPart, " "], equativeEndings.past.short);
const pastSubjunctive = addToForm([...pastPart, " "], equativeEndings.pastSubjunctive);
const wouldHaveBeen = addToForm([baParticle, " ", ...pastPart, " "], equativeEndings.pastSubjunctive);
const subjunctive = addToForm(
[...pastPart, " "],
equativeEndings.subjunctive
);
const future = addToForm(
[baParticle, " ", ...pastPart, " "],
equativeEndings.habitual
);
const wouldBe = addToForm(
[baParticle, " ", ...pastPart, " "],
equativeEndings.past.short
);
const pastSubjunctive = addToForm(
[...pastPart, " "],
equativeEndings.pastSubjunctive
);
const wouldHaveBeen = addToForm(
[baParticle, " ", ...pastPart, " "],
equativeEndings.pastSubjunctive
);
return {
halfPerfect, // Past Participle
past, // Past Participle + Past Equative
@ -436,29 +577,31 @@ function makePerfectContent(info: T.NonComboVerbInfo): T.PerfectContent {
}
function makePassiveContent(info: T.NonComboVerbInfo): {
imperfective: T.AspectContentPassive // --╖ ASPECT = "imperfective"
perfective: T.AspectContentPassive // --╜ ASPECT = "perfective"
imperfective: T.AspectContentPassive; // --╖ ASPECT = "imperfective"
perfective: T.AspectContentPassive; // --╜ ASPECT = "perfective"
perfect: T.PerfectContent;
} {
function makePassiveAspectContent(aspect: T.Aspect): T.AspectContentPassive {
if ("complement" in info && spaceInForm(info.root[aspect])) {
// seperated stative compound verb
const bridge = aspect === "imperfective"
const bridge =
aspect === "imperfective"
? noPersInfs(stativeAux.transitive.info.root.imperfective).long
: passiveStativeBridge;
const nonImperative = addToForm(
[info.complement, " ", bridge, " "],
stativeAux.intransitive[aspect].nonImperative,
stativeAux.intransitive[aspect].nonImperative
);
const future = addToForm([baParticle, " "], nonImperative);
const past = addToForm(
[info.complement, " ", bridge, " "],
stativeAux.intransitive[aspect].past,
stativeAux.intransitive[aspect].past
);
const habitualPast = addToForm([baParticle, " "], past);
const modal = makePassiveModalSection([
noPersInfs(info.root.imperfective).long, " ",
], stativeAux.intransitive.imperfective.modal);
const modal = makePassiveModalSection(
[noPersInfs(info.root.imperfective).long, " "],
stativeAux.intransitive.imperfective.modal
);
return {
imperative: undefined,
nonImperative,
@ -468,7 +611,8 @@ function makePassiveContent(info: T.NonComboVerbInfo): {
modal,
};
}
const root = (aspect === "imperfective")
const root =
aspect === "imperfective"
? removeAccents(noPersInfs(info.root[aspect]).long)
: noPersInfs(info.root[aspect]).long;
const aux = stativeAux.intransitive[aspect];
@ -477,7 +621,10 @@ function makePassiveContent(info: T.NonComboVerbInfo): {
const past = addToForm([root, " "], aux.past);
const habitualPast = addToForm([baParticle, " "], past);
const auxModal = aux.modal;
const modal = makePassiveModalSection([noPersInfs(info.root.imperfective).long, " "], auxModal);
const modal = makePassiveModalSection(
[noPersInfs(info.root.imperfective).long, " "],
auxModal
);
return {
imperative: undefined,
nonImperative, // ROOT LONG + kedulStat[aspect].nonImperative
@ -491,11 +638,12 @@ function makePassiveContent(info: T.NonComboVerbInfo): {
past: concatPsString(
removeAccents(noPersInfs(info.root.imperfective).long),
" ",
stativeAux.intransitive.info.participle.past as T.PsString,
stativeAux.intransitive.info.participle.past as T.PsString
),
present: { p: ا", f: "n / a" },
};
const perfect = (info.type === "stative compound")
const perfect =
info.type === "stative compound"
? makePassivePerfectContent(info)
: makePerfectContent({ ...info, participle: simpleVerbParticiple });
return {
@ -505,7 +653,18 @@ function makePassiveContent(info: T.NonComboVerbInfo): {
};
}
function makePassiveModalSection(base: Array<" " | T.SingleOrLengthOpts<T.PsString> | T.SingleOrLengthOpts<T.UnisexInflections> | T.SingleOrLengthOpts<T.PsString>[] | T.SingleOrLengthOpts<T.PsString[]> | T.OptionalPersonInflections<T.PsString> | T.VerbBlock>, auxModal: T.ModalContent): T.ModalContent {
function makePassiveModalSection(
base: Array<
| " "
| T.SingleOrLengthOpts<T.PsString>
| T.SingleOrLengthOpts<T.UnisexInflections>
| T.SingleOrLengthOpts<T.PsString>[]
| T.SingleOrLengthOpts<T.PsString[]>
| T.OptionalPersonInflections<T.PsString>
| T.VerbBlock
>,
auxModal: T.ModalContent
): T.ModalContent {
return {
nonImperative: addToForm(base, auxModal.nonImperative),
future: addToForm(base, auxModal.future),
@ -515,44 +674,73 @@ function makePassiveModalSection(base: Array<" " | T.SingleOrLengthOpts<T.PsStri
};
}
function makePassivePerfectContent(info: T.StativeCompoundVerbInfo): T.PerfectContent {
function makePassivePerfectContent(
info: T.StativeCompoundVerbInfo
): T.PerfectContent {
const pPart = stativeAux.intransitive.participle.past;
// will always be transitive
const halfPerfect = addToForm(
[info.complement, " ", passiveStativeBridge, " ", pPart],
emptyVerbBlock,
emptyVerbBlock
);
const past = addToForm(
[info.complement, " ", passiveStativeBridge, " ", pPart, " "],
equativeEndings.past.short,
equativeEndings.past.short
);
const present = addToForm(
[info.complement, " ", passiveStativeBridge, " ", pPart, " "],
equativeEndings.present,
equativeEndings.present
);
const habitual = addToForm(
[info.complement, " ", passiveStativeBridge, " ", pPart, " "],
equativeEndings.habitual,
equativeEndings.habitual
);
const subjunctive = addToForm(
[info.complement, " ", passiveStativeBridge, " ", pPart, " "],
equativeEndings.subjunctive,
equativeEndings.subjunctive
);
const future = addToForm(
[baParticle, " ", info.complement, " ", passiveStativeBridge, " ", pPart, " "],
equativeEndings.habitual,
[
baParticle,
" ",
info.complement,
" ",
passiveStativeBridge,
" ",
pPart,
" ",
],
equativeEndings.habitual
);
const wouldBe = addToForm(
[baParticle, " ", info.complement, " ", passiveStativeBridge, " ", pPart, " "],
equativeEndings.past.short,
[
baParticle,
" ",
info.complement,
" ",
passiveStativeBridge,
" ",
pPart,
" ",
],
equativeEndings.past.short
);
const pastSubjunctive = addToForm(
[info.complement, " ", passiveStativeBridge, " ", pPart, " "],
equativeEndings.pastSubjunctive,
equativeEndings.pastSubjunctive
);
const wouldHaveBeen = addToForm(
[baParticle, " ", info.complement, " ", passiveStativeBridge, " ", pPart, " "],
equativeEndings.pastSubjunctive,
[
baParticle,
" ",
info.complement,
" ",
passiveStativeBridge,
" ",
pPart,
" ",
],
equativeEndings.pastSubjunctive
);
return {
halfPerfect,
@ -567,7 +755,10 @@ function makePassivePerfectContent(info: T.StativeCompoundVerbInfo): T.PerfectCo
};
}
function enforceObject(conj: T.VerbConjugation, person: T.Person): T.VerbConjugation {
function enforceObject(
conj: T.VerbConjugation,
person: T.Person
): T.VerbConjugation {
const modifyPastInAspect = (as: T.AspectContent): T.AspectContent => ({
// WATCH OUT FOR DIFFERENCES WITH allOnePersonInflection (for the object w/ present tense)
// AND allOnePersonVerbForm (for the object w/ past tense object)
@ -584,7 +775,9 @@ function enforceObject(conj: T.VerbConjugation, person: T.Person): T.VerbConjuga
hypotheticalPast: allOnePersonVerbForm(as.modal.hypotheticalPast, person),
},
});
const modifyParticiple = (part: T.ParticipleContent): T.ParticipleContent => ({
const modifyParticiple = (
part: T.ParticipleContent
): T.ParticipleContent => ({
// TODO: What to do with this!
present: allOnePersonInflection(part.present, person),
past: chooseParticipleInflection(part.past, person),
@ -600,7 +793,9 @@ function enforceObject(conj: T.VerbConjugation, person: T.Person): T.VerbConjuga
pastSubjunctive: allOnePersonVerbForm(perf.pastSubjunctive, person),
wouldHaveBeen: allOnePersonVerbForm(perf.wouldHaveBeen, person),
});
const modifyPassiveAspect = (as: T.AspectContentPassive): T.AspectContentPassive => ({
const modifyPassiveAspect = (
as: T.AspectContentPassive
): T.AspectContentPassive => ({
imperative: undefined,
nonImperative: allOnePersonVerbForm(as.nonImperative, person),
future: allOnePersonVerbForm(as.future, person),
@ -620,13 +815,15 @@ function enforceObject(conj: T.VerbConjugation, person: T.Person): T.VerbConjuga
perfective: modifyPastInAspect(conj.perfective),
participle: modifyParticiple(conj.participle),
perfect: modifyPerfect(conj.perfect),
...conj.passive ? {
...(conj.passive
? {
passive: {
imperfective: modifyPassiveAspect(conj.passive.imperfective),
perfective: modifyPassiveAspect(conj.passive.perfective),
perfect: modifyPerfect(conj.passive.perfect),
},
}
} : {},
: {}),
};
}
@ -635,18 +832,29 @@ function enforceObject(conj: T.VerbConjugation, person: T.Person): T.VerbConjuga
function finishSimpleVerbPast(
info: T.NonComboVerbInfo,
aspect: T.Aspect,
roughPast: T.LengthOptions<T.VerbBlock>,
roughPast: T.LengthOptions<T.VerbBlock>
): T.VerbForm {
const applyAccent = (block: T.VerbBlock, form: "short" | "long"): T.VerbBlock => (
mapVerbBlock((item: T.PsString, rowNum: number | undefined, colNum: number | undefined) => {
const nonRedundantLEnding = (
(rowNum === 4 && colNum === 1) &&
const applyAccent = (
block: T.VerbBlock,
form: "short" | "long"
): T.VerbBlock =>
mapVerbBlock(
(
item: T.PsString,
rowNum: number | undefined,
colNum: number | undefined
) => {
const nonRedundantLEnding =
rowNum === 4 &&
colNum === 1 &&
item.p.slice(-1) === "ل" &&
["ul", "úl"].includes(item.f.slice(-2))
)
const n = (((form === "short") || nonRedundantLEnding) ? 0 : 1) + (info.yulEnding ? 1 : 0);
["ul", "úl"].includes(item.f.slice(-2));
const n =
(form === "short" || nonRedundantLEnding ? 0 : 1) +
(info.yulEnding ? 1 : 0);
return accentOnNFromEnd(item, n);
}, block)
},
block
);
const short = ensureShort3rdPersMascSing(info, aspect, roughPast.short);
if (aspect === "imperfective") {
@ -663,29 +871,24 @@ function finishSimpleVerbPast(
function ensureShort3rdPersMascSing(
info: T.NonComboVerbInfo,
aspect: T.Aspect,
block: T.VerbBlock,
block: T.VerbBlock
): T.VerbBlock {
const replace3rdPersMascSing = (
replacement: T.ArrayOneOrMore<T.PsString>,
block: T.VerbBlock,
): T.VerbBlock => ([
...block.slice(0, 4),
[replacement, block[4][1]],
block[5],
] as T.VerbBlock);
block: T.VerbBlock
): T.VerbBlock =>
[...block.slice(0, 4), [replacement, block[4][1]], block[5]] as T.VerbBlock;
const makeAawuForm = (root: T.PsString): T.PsString => {
const base = {
p: root.p.slice(0, -1),
f: root.f.slice(0, -2),
};
return concatPsString(base, { p: "اوه", f: "aawu" });
}
};
const infinitive = noPersInfs(info.root.imperfective).long;
const endsInAwul = (
(["awul", "awúl"].includes(infinitive.f.slice(-4)))
&&
(infinitive.p.slice(-2) === "ول")
);
const endsInAwul =
["awul", "awúl"].includes(infinitive.f.slice(-4)) &&
infinitive.p.slice(-2) === "ول";
if (endsInAwul) {
const root = noPersInfs(info.root[aspect]).short;
return replace3rdPersMascSing([makeAawuForm(root)], block);
@ -694,7 +897,8 @@ function ensureShort3rdPersMascSing(
const form = info.idiosyncraticThirdMascSing[aspect];
// if it ends in a consonant, the special form will also have another
// variation ending with a ه - u
const endsInAConsonant = (pashtoConsonants.includes(form.p.slice(-1)) || form.f.slice(-1) === "w");
const endsInAConsonant =
pashtoConsonants.includes(form.p.slice(-1)) || form.f.slice(-1) === "w";
const replacement: T.ArrayOneOrMore<T.PsString> = endsInAConsonant
? [
form,
@ -711,24 +915,28 @@ function ensureShort3rdPersMascSing(
function accentImperfectiveModalRootAndTail(
info: T.NonComboVerbInfo,
rt: T.SingleOrLengthOpts<T.PsString>,
length?: "long" | "short",
length?: "long" | "short"
): T.SingleOrLengthOpts<T.PsString> {
if ("long" in rt) {
return {
short: accentImperfectiveModalRootAndTail(info, rt.short, "short") as T.PsString,
long: accentImperfectiveModalRootAndTail(info, rt.long, "long") as T.PsString,
short: accentImperfectiveModalRootAndTail(
info,
rt.short,
"short"
) as T.PsString,
long: accentImperfectiveModalRootAndTail(
info,
rt.long,
"long"
) as T.PsString,
};
}
}
const n = info.yulEnding
? 2
: length === "short"
? 0
: 1;
const n = info.yulEnding ? 2 : length === "short" ? 0 : 1;
return accentOnNFromEnd(rt, n);
}
function getTransitivity(info: T.VerbInfo): "transitive" | "intransitive" {
return ("transitivity" in info && info.transitivity === "intransitive")
return "transitivity" in info && info.transitivity === "intransitive"
? "intransitive"
: "transitive";
}

View File

@ -693,7 +693,7 @@ export type VPRendered = {
servant: "subject" | "object" | undefined;
isPast: boolean;
isTransitive: boolean;
isCompound: "stative" | "dynamic" | false;
isCompound: "stative" | "dynamic" | "generative stative" | false;
blocks: Block[][];
kids: Kid[];
englishBase?: string[];
@ -811,7 +811,7 @@ export type VerbSelection = {
transitivity: Transitivity;
canChangeTransitivity: boolean;
canChangeStatDyn: boolean;
isCompound: "stative" | "dynamic" | false;
isCompound: "stative" | "dynamic" | "generative stative" | false;
voice: Voice;
canChangeVoice: boolean;
negative: boolean;
@ -867,6 +867,7 @@ export type NounSelection = {
number: NounNumber;
numberCanChange: boolean;
dynamicComplement?: boolean;
genStativeComplement?: boolean;
adjectives: AdjectiveSelection[];
possesor: undefined | PossesorSelection;
demonstrative: undefined | DemonstrativeSelection;
@ -1199,7 +1200,6 @@ export type MiniPronoun = {
export type RenderVerbOutput = {
hasBa: boolean;
vbs: VerbRenderedOutput;
objComp: Rendered<NPSelection> | undefined;
};
export type VerbRenderedOutput = [[VHead] | [], [VB, VBE] | [VBE]];
export type RootsStemsOutput = [[VHead] | [], [VB, VBA] | [VBA]]; // or perfect / equative

View File

@ -7,6 +7,8 @@
*/
module.exports = [
1527818320, // جارو کول - to sweep
1658796089458, // استري کول - to iron
1527816643, // استعفا کول - to resign, to quit (a job or position)
1527817823, // اصرار کول - to insist, persist, demand
1591002320547, // امر کول - to order, command
@ -139,4 +141,4 @@ module.exports = [
1609599425410, // دعا کول - to pray
1527812939, // منډې وهل - to run
1614602054303, // بدله اخیستل - to take revenge
]
];

View File

@ -7,6 +7,7 @@
*/
module.exports = [
1608137130992, // چیغه کول
1658537998960, // لېونی کول
1527812403, // بچ کول - to save, protect, guard, spare, rescue, economize
1577299232429, // بدلول - to change, to adapt, exchange, replace
@ -83,4 +84,4 @@ module.exports = [
1527816559, // یادول - to remember, to recall, to think on, to call
1527813556, // یو ځای کول - to gather, bring together
1527815444, // زده کول - to learn, to teach
]
];

View File

@ -5427,7 +5427,7 @@ forwarded@0.2.0:
fp-ts@^2.16.0:
version "2.16.0"
resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-2.16.0.tgz#64e03314dfc1c7ce5e975d3496ac14bc3eb7f92e"
resolved "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.0.tgz"
integrity sha512-bLq+KgbiXdTEoT1zcARrWEpa5z6A/8b7PcDW7Gef3NSisQ+VS7ll2Xbf1E+xsgik0rWub/8u0qP/iTTjj+PhxQ==
fragment-cache@^0.2.1:
@ -9544,11 +9544,6 @@ 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"