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, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"fp-ts": "^2.16.0",
"react-select": "^5.4.0" "react-select": "^5.4.0"
}, },
"devDependencies": { "devDependencies": {
@ -10735,6 +10736,11 @@
"node": ">= 0.6" "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": { "node_modules/fragment-cache": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
@ -31006,6 +31012,11 @@
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"dev": true "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": { "fragment-cache": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",

View File

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

View File

@ -9,9 +9,7 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import ButtonSelect from "./components/src/ButtonSelect"; import ButtonSelect from "./components/src/ButtonSelect";
import { import { Modal } from "react-bootstrap";
Modal
} from "react-bootstrap";
import * as T from "./types"; import * as T from "./types";
import defualtTextOptions from "./lib/src/default-text-options"; import defualtTextOptions from "./lib/src/default-text-options";
import useStickyState from "./components/src/useStickyState"; import useStickyState from "./components/src/useStickyState";
@ -23,148 +21,196 @@ import InflectionDemo from "./demo-components/InflectionDemo";
import SpellingDemo from "./demo-components/SpellingDemo"; import SpellingDemo from "./demo-components/SpellingDemo";
function App() { function App() {
const [showingTextOptions, setShowingTextOptions] = useStickyState<boolean>(false, "showTextOpts1"); const [showingTextOptions, setShowingTextOptions] = useStickyState<boolean>(
const [textOptions, setTextOptions] = useStickyState<T.TextOptions>(defualtTextOptions, "textOpts1"); false,
const [theme, setTheme] = useStickyState<"light" | "dark">("light", "theme1"); "showTextOpts1"
const [showing, setShowing] = useState<string>(""); );
function handleHiderClick(label: string) { const [textOptions, setTextOptions] = useStickyState<T.TextOptions>(
setShowing(os => os === label defualtTextOptions,
? "" "textOpts1"
: label); );
} const [theme, setTheme] = useStickyState<"light" | "dark">("light", "theme1");
useEffect(() => { const [showing, setShowing] = useState<string>("");
document.documentElement.setAttribute("data-theme", theme); function handleHiderClick(label: string) {
}, [theme]); 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" }}> <main className="flex-shrink-0 mb-4">
<div style={{ position: "absolute", top: "1.5rem", right: "1.5rem", display: "flex", flexDirection: "row" }}> <div className="container" style={{ maxWidth: "800px" }}>
<div <div
className="clickable mr-3" style={{
onClick={() => setTheme(theme === "light" ? "dark" : "light")} position: "absolute",
> top: "1.5rem",
<i className={`fa-lg fas fa-${theme === "light" ? "sun" : "moon"}`} /> right: "1.5rem",
</div> display: "flex",
<div flexDirection: "row",
className="clickable" }}
onClick={() => setShowingTextOptions(true)} >
> <div
<i className="fa-lg fas fa-cog" /> className="clickable mr-3"
</div> onClick={() => setTheme(theme === "light" ? "dark" : "light")}
>
<i
className={`fa-lg fas fa-${theme === "light" ? "sun" : "moon"}`}
/>
</div> </div>
<div className="text-center mb-4" style={{ marginTop: "3rem", marginBottom: "1rem" }}> <div
<h1 className="display-4 mt-2"><code>Pashto Inflector</code></h1> className="clickable"
<p className="lead my-3" style={{ maxWidth: "600px", margin: "0 auto" }}> onClick={() => setShowingTextOptions(true)}
An open source TypeScript/React library for Pashto inflection, verb conjugation, phrase generation, text conversion, and more >
</p> <i className="fa-lg fas fa-cog" />
<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
label="Verb Conjugation / Verb Phrase Engine"
hLevel={3}
showing={showing === "verbs"}
handleChange={() => handleHiderClick("verbs")}
>
<VPBuilderDemo opts={textOptions} />
</Hider>
<Hider
label="Equative Phrase Engine"
hLevel={3}
showing={showing === "equatives"}
handleChange={() => handleHiderClick("equatives")}
>
<div className="mt-4" style={{ paddingBottom: "20px" }}>
<EPExplorer
opts={textOptions}
entryFeeder={entryFeeder}
/>
</div>
</Hider>
<Hider
label="Inflection Engine"
hLevel={3}
showing={showing === "inflection"}
handleChange={() => handleHiderClick("inflection")}
>
<InflectionDemo opts={textOptions} />
</Hider>
<Hider
label="Spelling Conversion / Diacritics Engine"
hLevel={3}
showing={showing === "spelling"}
handleChange={() => handleHiderClick("spelling")}
>
<SpellingDemo opts={textOptions} onChange={setTextOptions} />
</Hider>
</div> </div>
</main> </div>
<Modal show={showingTextOptions} onHide={() => setShowingTextOptions(false)}> <div
<Modal.Header closeButton> className="text-center mb-4"
<Modal.Title>Settings</Modal.Title> style={{ marginTop: "3rem", marginBottom: "1rem" }}
</Modal.Header> >
<Modal.Body> <h1 className="display-4 mt-2">
<h6>Pashto Spelling</h6> <code>Pashto Inflector</code>
<ButtonSelect </h1>
options={[ <p
{ label: "Afghan", value: "Afghan" }, className="lead my-3"
{ label: "Pakistani ي", value: "Pakistani ي" }, style={{ maxWidth: "600px", margin: "0 auto" }}
{ label: "Pakistani ی", value: "Pakistani ی" }, >
]} An open source TypeScript/React library for Pashto inflection,
value={textOptions.spelling} verb conjugation, phrase generation, text conversion, and more
handleChange={(p) => { </p>
setTextOptions({ <p>
...textOptions, Used in the{" "}
spelling: p, <a href="https://dictionary.lingdocs.com">
}); LingDocs Pashto Dictionary
}} </a>{" "}
/> and{" "}
<h6 className="mt-3">Diacritics</h6> <a href="https://grammar.lingdocs.com">LingDocs Pashto Grammar</a>
<ButtonSelect </p>
options={[ <p>
{ label: "On", value: "true" }, by <a href="https://adueck.github.io">Adam Dueck</a> - GPLv3
{ label: "Off", value: "false" }, licensed{" "}
]} <a href="https://github.com/lingdocs/pashto-inflector">
value={textOptions.diacritics.toString()} Source Code
handleChange={(p) => setTextOptions({ ...textOptions, diacritics: p === "true" })} </a>{" "}
/> on GitHub
<h6 className="mt-3">Pashto Text Size</h6> </p>
<ButtonSelect </div>
options={[ <h2 className="mb-3">Demos:</h2>
{ label: "Normal", value: "normal" }, <Hider
{ label: "Large", value: "larger" }, label="Verb Conjugation / Verb Phrase Engine"
{ label: "X-Large", value: "largest" }, hLevel={3}
]} showing={showing === "verbs"}
value={textOptions.pTextSize} handleChange={() => handleHiderClick("verbs")}
handleChange={(p) => setTextOptions({ ...textOptions, pTextSize: p })} >
/> <VPBuilderDemo opts={textOptions} />
<h6 className="mt-3">Phonetics</h6> </Hider>
<ButtonSelect <Hider
options={[ label="Equative Phrase Engine"
{ label: "LingDocs", value: "lingdocs" }, hLevel={3}
{ label: "IPA", value: "ipa" }, showing={showing === "equatives"}
{ label: "ALAC", value: "alalc" }, handleChange={() => handleHiderClick("equatives")}
// { label: "None", value: "none" }, >
]} <div className="mt-4" style={{ paddingBottom: "20px" }}>
value={textOptions.phonetics} <EPExplorer opts={textOptions} entryFeeder={entryFeeder} />
handleChange={(p) => setTextOptions({ ...textOptions, phonetics: p })} </div>
/> </Hider>
</Modal.Body> <Hider
<Modal.Footer> label="Inflection Engine"
<button type="button" className="btn btn-primary clb" onClick={() => setShowingTextOptions(false)}> hLevel={3}
Close showing={showing === "inflection"}
</button> handleChange={() => handleHiderClick("inflection")}
</Modal.Footer> >
</Modal> <InflectionDemo opts={textOptions} />
{/* <footer className="footer mt-auto py-3" style={{ backgroundColor: "#f5f5f5" }}> </Hider>
<Hider
label="Spelling Conversion / Diacritics Engine"
hLevel={3}
showing={showing === "spelling"}
handleChange={() => handleHiderClick("spelling")}
>
<SpellingDemo opts={textOptions} onChange={setTextOptions} />
</Hider>
</div>
</main>
<Modal
show={showingTextOptions}
onHide={() => setShowingTextOptions(false)}
>
<Modal.Header closeButton>
<Modal.Title>Settings</Modal.Title>
</Modal.Header>
<Modal.Body>
<h6>Pashto Spelling</h6>
<ButtonSelect
options={[
{ label: "Afghan", value: "Afghan" },
{ label: "Pakistani ي", value: "Pakistani ي" },
{ label: "Pakistani ی", value: "Pakistani ی" },
]}
value={textOptions.spelling}
handleChange={(p) => {
setTextOptions({
...textOptions,
spelling: p,
});
}}
/>
<h6 className="mt-3">Diacritics</h6>
<ButtonSelect
options={[
{ label: "On", value: "true" },
{ label: "Off", value: "false" },
]}
value={textOptions.diacritics.toString()}
handleChange={(p) =>
setTextOptions({ ...textOptions, diacritics: p === "true" })
}
/>
<h6 className="mt-3">Pashto Text Size</h6>
<ButtonSelect
options={[
{ label: "Normal", value: "normal" },
{ label: "Large", value: "larger" },
{ label: "X-Large", value: "largest" },
]}
value={textOptions.pTextSize}
handleChange={(p) =>
setTextOptions({ ...textOptions, pTextSize: p })
}
/>
<h6 className="mt-3">Phonetics</h6>
<ButtonSelect
options={[
{ label: "LingDocs", value: "lingdocs" },
{ label: "IPA", value: "ipa" },
{ label: "ALAC", value: "alalc" },
// { label: "None", value: "none" },
]}
value={textOptions.phonetics}
handleChange={(p) =>
setTextOptions({ ...textOptions, phonetics: p })
}
/>
</Modal.Body>
<Modal.Footer>
<button
type="button"
className="btn btn-primary clb"
onClick={() => setShowingTextOptions(false)}
>
Close
</button>
</Modal.Footer>
</Modal>
{/* <footer className="footer mt-auto py-3" style={{ backgroundColor: "#f5f5f5" }}>
<div className="container"> <div className="container">
<span className="text-muted">Copyright © 2022 <a href="https://www.lingdocs.com">lingdocs.com</a> - <a href="https://github.com/lingdocs/pashto-inflector/blob/master/LICENSE">MIT License</a></span> <span className="text-muted">Copyright © 2022 <a href="https://www.lingdocs.com">lingdocs.com</a> - <a href="https://github.com/lingdocs/pashto-inflector/blob/master/LICENSE">MIT License</a></span>
</div> </div>
</footer> */} </footer> */}
</> </>
);
} }
export default App; export default App;

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

File diff suppressed because it is too large Load Diff

View File

@ -47,4 +47,4 @@
"babel-loader": "^8.3.0", "babel-loader": "^8.3.0",
"fs-extra": "^10.1.0" "fs-extra": "^10.1.0"
} }
} }

View File

@ -1,6 +1,4 @@
import { import { makeNounSelection } from "../../../lib/src/phrase-building/make-selections";
makeNounSelection,
} from "../../../lib/src/phrase-building/make-selections";
import * as T from "../../../types"; import * as T from "../../../types";
import ButtonSelect from "../ButtonSelect"; import ButtonSelect from "../ButtonSelect";
@ -57,45 +55,49 @@ import AdjectiveManager from "./AdjectiveManager";
// } // }
function NPNounPicker(props: { function NPNounPicker(props: {
entryFeeder: T.EntryFeeder, entryFeeder: T.EntryFeeder;
noun: T.NounSelection | undefined, noun: T.NounSelection | undefined;
onChange: (p: T.NounSelection | undefined) => void, onChange: (p: T.NounSelection | undefined) => void;
opts: T.TextOptions, opts: T.TextOptions;
phraseIsComplete: boolean, phraseIsComplete: boolean;
}) { }) {
// const [patternFilter, setPatternFilter] = useState<FilterPattern | undefined>(undefined); console.log({ noun: props.noun });
// const [showFilter, setShowFilter] = useState<boolean>(false) // const [patternFilter, setPatternFilter] = useState<FilterPattern | undefined>(undefined);
// const nounsFiltered = props.nouns // const [showFilter, setShowFilter] = useState<boolean>(false)
// .filter(nounFilter(patternFilter)) // const nounsFiltered = props.nouns
// .sort((a, b) => (a.p.localeCompare(b.p, "af-PS"))); // .filter(nounFilter(patternFilter))
function onEntrySelect(entry: T.NounEntry | undefined) { // .sort((a, b) => (a.p.localeCompare(b.p, "af-PS")));
if (!entry) { function onEntrySelect(entry: T.NounEntry | undefined) {
return props.onChange(undefined); if (!entry) {
} return props.onChange(undefined);
props.onChange(makeNounSelection(entry, props.noun));
} }
// function handleFilterClose() { props.onChange(makeNounSelection(entry, props.noun));
// setPatternFilter(undefined); }
// setShowFilter(false); // function handleFilterClose() {
// } // setPatternFilter(undefined);
function handelAdjectivesUpdate(adjectives: T.AdjectiveSelection[]) { // setShowFilter(false);
if (props.noun) { // }
props.onChange({ function handelAdjectivesUpdate(adjectives: T.AdjectiveSelection[]) {
...props.noun, if (props.noun) {
adjectives, props.onChange({
}); ...props.noun,
} adjectives,
});
} }
function handleDemonstrativeUpdate(demonstrative: undefined | T.NounSelection["demonstrative"]) { }
if (props.noun) { function handleDemonstrativeUpdate(
props.onChange({ demonstrative: undefined | T.NounSelection["demonstrative"]
...props.noun, ) {
demonstrative, if (props.noun) {
}); props.onChange({
} ...props.noun,
demonstrative,
});
} }
return <div style={{ maxWidth: "225px", minWidth: "125px" }}> }
{/* {showFilter && <div className="mb-2 text-center"> 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="d-flex flex-row justify-content-between">
<div className="text-small mb-1">Filter by inflection pattern</div> <div className="text-small mb-1">Filter by inflection pattern</div>
<div className="clickable" onClick={handleFilterClose}>X</div> <div className="clickable" onClick={handleFilterClose}>X</div>
@ -108,72 +110,102 @@ function NPNounPicker(props: {
handleChange={setPatternFilter} handleChange={setPatternFilter}
/> />
</div>} */} </div>} */}
{props.noun && <AdjectiveManager {props.noun && (
phraseIsComplete={props.phraseIsComplete} <AdjectiveManager
adjectives={props.noun?.adjectives} phraseIsComplete={props.phraseIsComplete}
demonstrative={props.noun.demonstrative} adjectives={props.noun?.adjectives}
entryFeeder={props.entryFeeder} demonstrative={props.noun.demonstrative}
entryFeeder={props.entryFeeder}
opts={props.opts}
onChange={handelAdjectivesUpdate}
onDemonstrativeChange={handleDemonstrativeUpdate}
/>
)}
<div className="h6">Noun</div>
{!(
props.noun &&
(props.noun.dynamicComplement || props.noun.genStativeComplement)
) ? (
<div>
<EntrySelect
value={props.noun?.entry}
entryFeeder={props.entryFeeder.nouns}
onChange={onEntrySelect}
name="Noun"
opts={props.opts} opts={props.opts}
onChange={handelAdjectivesUpdate} />
onDemonstrativeChange={handleDemonstrativeUpdate} </div>
/>} ) : (
<div className="h6">Noun</div> <div>
{!(props.noun && props.noun.dynamicComplement) ? <div> {props.noun && (
<EntrySelect
value={props.noun?.entry}
entryFeeder={props.entryFeeder.nouns}
onChange={onEntrySelect}
name="Noun"
opts={props.opts}
/>
</div> : <div>
{props.noun && <div>
<div className="mb-2">Included in 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.genderCanChange ? <ButtonSelect <div className="mb-2">
small Included in{" "}
options={[ {props.noun.genStativeComplement ? "Gen. Stat." : "Dyn."}{" "}
{ label: "Masc", value: "masc" }, Compound:
{ label: "Fem", value: "fem" }, </div>
]} <div className="mb-3 text-center">
value={props.noun.gender} <InlinePs opts={props.opts}>
handleChange={(gender) => { {{ p: props.noun.entry.p, f: props.noun.entry.f }}
if (!props.noun || !props.noun.genderCanChange) return; </InlinePs>
props.onChange({ <div className="text-muted">{props.noun.entry.e}</div>
...props.noun, </div>
gender,
});
}}
/> : props.noun.gender === "masc" ? "Masc." : "Fem."}
</div> </div>
<div> )}
{props.noun.numberCanChange ? <ButtonSelect </div>
small )}
options={[ {props.noun && (
{ label: "Sing.", value: "singular" }, <div className="my-2 d-flex flex-row justify-content-around align-items-center">
{ label: "Plur.", value: "plural" }, <div>
]} {props.noun.genderCanChange ? (
value={props.noun.number} <ButtonSelect
handleChange={(number) => { small
if (!props.noun || !props.noun.numberCanChange) return; options={[
props.onChange({ { label: "Masc", value: "masc" },
...props.noun, { label: "Fem", value: "fem" },
number, ]}
}); value={props.noun.gender}
}} handleChange={(gender) => {
/> : props.noun.number === "singular" ? "Sing." : "Plur."} if (!props.noun || !props.noun.genderCanChange) return;
</div> props.onChange({
</div>} ...props.noun,
</div>; gender,
});
}}
/>
) : props.noun.gender === "masc" ? (
"Masc."
) : (
"Fem."
)}
</div>
<div>
{props.noun.numberCanChange ? (
<ButtonSelect
small
options={[
{ label: "Sing.", value: "singular" },
{ label: "Plur.", value: "plural" },
]}
value={props.noun.number}
handleChange={(number) => {
if (!props.noun || !props.noun.numberCanChange) return;
props.onChange({
...props.noun,
number,
});
}}
/>
) : props.noun.number === "singular" ? (
"Sing."
) : (
"Plur."
)}
</div>
</div>
)}
</div>
);
} }
export default NPNounPicker; export default NPNounPicker;

View File

@ -9,13 +9,21 @@ import Hider from "../Hider";
import * as T from "../../../types"; import * as T from "../../../types";
import useStickyState from "../useStickyState"; import useStickyState from "../useStickyState";
import { isImperativeTense } from "../../../lib/src/type-predicates"; 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"; // import { conjugateVerb } from "../../../lib/src/verb-conjugation";
function AllTensesDisplay({ function AllTensesDisplay({
VS, VS,
opts, opts,
onChange,
object,
}: { }: {
onChange: (p: VpsReducerAction) => void;
VS: T.VerbSelection; VS: T.VerbSelection;
object: T.NPSelection | T.ObjectNP | undefined;
opts: T.TextOptions; opts: T.TextOptions;
}) { }) {
const [showing, setShowing] = useStickyState<string[]>([], "VPTensesShowing"); const [showing, setShowing] = useStickyState<string[]>([], "VPTensesShowing");
@ -23,6 +31,11 @@ function AllTensesDisplay({
false, false,
"showFormulasWithCharts" "showFormulasWithCharts"
); );
const [objectNPNumber, setObjectNPNumber] = useState<T.NounNumber>(
object && typeof object === "object" && object.selection.type === "noun"
? object.selection.number
: "singular"
);
const adjustShowing = (v: string) => { const adjustShowing = (v: string) => {
if (showing.includes(v)) { if (showing.includes(v)) {
setShowing((os) => os.filter((x) => x !== v)); setShowing((os) => os.filter((x) => x !== v));
@ -56,14 +69,69 @@ function AllTensesDisplay({
? (`${baseTense}Modal` as T.AbilityTense) ? (`${baseTense}Modal` as T.AbilityTense)
: baseTense; : 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 ( return (
<div> <div>
<div <div
className="clickable mb-2 small text-center"
onClick={() => setShowFormulas((x) => !x)} onClick={() => setShowFormulas((x) => !x)}
className="small clickable text-right mb-2"
> >
🧪 {!showFormulas ? "Show" : "Hide"} Formulas 🧪 {!showFormulas ? "Show" : "Hide"} Formulas
</div> </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) => { {options.map((tense) => {
const t = getTense(tense.value); const t = getTense(tense.value);
return ( return (
@ -81,7 +149,20 @@ function AllTensesDisplay({
)} )}
{showing && ( {showing && (
<ChartDisplay <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} negative={VS.negative}
tense={t} tense={t}
transitivity={VS.transitivity} transitivity={VS.transitivity}

View File

@ -8,7 +8,13 @@ import { choosePersInf, getLength } from "../../../lib/src/p-text-helpers";
import genderColors from "../gender-colors"; import genderColors from "../gender-colors";
import { eqPsStringWVars } from "../../../lib/src/fp-ps"; import { eqPsStringWVars } from "../../../lib/src/fp-ps";
import PersInfsPicker from "../PersInfsPicker"; 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 = { export const roleIcon = {
king: <i className="mx-1 fas fa-crown" />, king: <i className="mx-1 fas fa-crown" />,
@ -21,14 +27,21 @@ function VerbChartDisplay({
shortDefault, shortDefault,
transitivity, transitivity,
past, past,
negative,
imperative,
}: { }: {
chart: T.OptionalPersonInflections< chart: {
T.SingleOrLengthOpts<T.RenderVerbOutput[]> objNP: T.Rendered<T.NPSelection> | undefined;
>; vbs: T.OptionalPersonInflections<
T.SingleOrLengthOpts<T.RenderVerbOutput[]>
>;
};
opts: T.TextOptions; opts: T.TextOptions;
shortDefault?: boolean; shortDefault?: boolean;
transitivity: T.Transitivity; transitivity: T.Transitivity;
past: boolean; past: boolean;
negative: boolean;
imperative: boolean;
}) { }) {
const [length, setLength] = useState<T.Length>( const [length, setLength] = useState<T.Length>(
shortDefault ? "short" : "long" shortDefault ? "short" : "long"
@ -37,13 +50,23 @@ function VerbChartDisplay({
useEffect(() => { useEffect(() => {
setLength("long"); setLength("long");
}, [chart]); }, [chart]);
const desktop = useMediaPredicate("(min-width: 600px)"); const chartWPers = choosePersInf(chart.vbs, persInf);
const chartWPers = choosePersInf(chart, persInf);
const chartWLength = getLength(chartWPers, length); 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 = const verbBlock =
x.length === 12 x.length === 1
? // unchanging gramm trans or dynamic compound past transitive
[[[x[0]]]]
: x.length === 12
? [ ? [
// 1st pers // 1st pers
[ [
@ -83,10 +106,15 @@ function VerbChartDisplay({
</div> </div>
<div className="row"> <div className="row">
<div className="col"> <div className="col">
<AgreementInfo transitivity={transitivity} past={past} /> <AgreementInfo
opts={opts}
transitivity={transitivity}
past={past}
objNP={chart.objNP}
/>
</div> </div>
<div className="col"> <div className="col">
{"long" in chartWPers && ( {"long" in chartWPers && lengthsMakeADiff(chartWPers) && (
<LengthSelection <LengthSelection
hasMini={"mini" in chartWPers} hasMini={"mini" in chartWPers}
value={length} value={length}
@ -94,30 +122,36 @@ function VerbChartDisplay({
/> />
)} )}
</div> </div>
{desktop && <div className="col" />} <div className="hide-on-mobile col" />
</div> </div>
<table className="table mt-2" style={{ tableLayout: "fixed" }}> {verbBlock.length === 1 ? (
<thead> <div className="d-flex flex-row justify-content-center mt-3 text-center">
<tr> <TableCell item={verbBlock[0][0][0]} textOptions={opts} />
<th scope="col" style={{ width: "3rem" }}> </div>
Pers. ) : (
</th> <table className="table mt-2" style={{ tableLayout: "fixed" }}>
<th scope="col">Singular</th> <thead>
<th scope="col">Plural</th> <tr>
</tr> <th scope="col" style={{ width: "3rem" }}>
</thead> Pers.
<tbody> </th>
{verbBlock.map((personRow, i) => ( <th scope="col">Singular</th>
<PersonRow <th scope="col">Plural</th>
key={Math.random()} </tr>
// get proper version for imperative blocks </thead>
person={verbBlock.length === 1 ? 1 : i} <tbody>
item={personRow} {verbBlock.map((personRow, i) => (
opts={opts} <PersonRow
/> key={Math.random()}
))} // get proper version for imperative blocks
</tbody> person={verbBlock.length === 1 ? 1 : i}
</table> item={personRow}
opts={opts}
/>
))}
</tbody>
</table>
)}
</> </>
); );
} }
@ -224,32 +258,93 @@ function LengthSelection({
); );
} }
function renderVerbOutputToText(negative: boolean) { function renderVerbOutputToText({
objNP,
negative,
hasBa,
imperative,
intransitive,
}: {
objNP: T.Rendered<T.NPSelection> | undefined;
negative: boolean;
hasBa: boolean;
imperative: boolean;
intransitive: boolean;
}) {
return function (v: T.RenderVerbOutput): T.PsString[] { return function (v: T.RenderVerbOutput): T.PsString[] {
const blocks = insertNegative( const blocks: T.Block[][] = insertNegative(v.vbs, negative, imperative).map(
v.vbs, (b) => {
negative, if (!objNP) return b;
false /* TODO: apply imperative */ 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({ function AgreementInfo({
transitivity, transitivity,
past, past,
objNP,
opts,
}: { }: {
transitivity: T.Transitivity; transitivity: T.Transitivity;
past: boolean; 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 ( return (
<div className="text-muted small mt-1"> <div className="text-muted small mt-1">
{roleIcon.king} agrees w/{" "} <div>
<strong> {roleIcon.king} agrees w/{" "}
{transitivity !== "intransitive" && past ? "object" : "subject"} <strong>
</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> </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; export default VerbChartDisplay;

View File

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

View File

@ -28,213 +28,244 @@ export const vpPhraseURLParam = "vp";
// TODO: error handling on error with rendering etc // TODO: error handling on error with rendering etc
function VPExplorer(props: { function VPExplorer(props: {
loaded?: T.VPSelectionState, loaded?: T.VPSelectionState;
verb: T.VerbEntry, verb: T.VerbEntry;
opts: T.TextOptions, opts: T.TextOptions;
handleLinkClick: ((ts: number) => void) | "none", handleLinkClick: ((ts: number) => void) | "none";
entryFeeder: T.EntryFeeder, entryFeeder: T.EntryFeeder;
onlyPhrases?: boolean, onlyPhrases?: boolean;
}) { }) {
const [vps, adjustVps] = useStickyReducer( const [vps, adjustVps] = useStickyReducer(
vpsReducer, vpsReducer,
props.loaded props.loaded
? props.loaded ? props.loaded
: savedVps => makeVPSelectionState(props.verb, savedVps), : (savedVps) => makeVPSelectionState(props.verb, savedVps),
"vpsState16", "vpsState16",
flashMessage, flashMessage
); );
const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">( const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">(
savedMode => { (savedMode) => {
if (!savedMode) return props.onlyPhrases ? "phrases" : "charts"; if (!savedMode) return props.onlyPhrases ? "phrases" : "charts";
if (savedMode === "quiz") return "phrases"; if (savedMode === "quiz") return "phrases";
return savedMode; return savedMode;
}, },
"verbExplorerMode2", "verbExplorerMode2"
); );
const [showClipped, setShowClipped] = useState<string>(""); const [showClipped, setShowClipped] = useState<string>("");
const [ const [
alertMsg, alertMsg,
// setAlertMsg, // setAlertMsg,
] = useState<string | undefined>(undefined); ] = useState<string | undefined>(undefined);
function flashMessage(msg: string) { function flashMessage(msg: string) {
console.log(msg); console.log(msg);
// for some crazy reason this causes it to go through with the state change // for some crazy reason this causes it to go through with the state change
// we're trying to avoid when there's a potential errored state // we're trying to avoid when there's a potential errored state
// setAlertMsg(msg); // setAlertMsg(msg);
// setTimeout(() => { // setTimeout(() => {
// setAlertMsg(undefined); // setAlertMsg(undefined);
// }, 2000); // }, 2000);
}
useEffect(() => {
adjustVps({
type: "set verb",
payload: props.verb,
});
// eslint-disable-next-line
}, [props.verb]);
useEffect(() => {
if (props.loaded) {
adjustVps({
type: "load vps",
payload: props.loaded,
});
} }
useEffect(() => { // eslint-disable-next-line
adjustVps({ }, [props.loaded]);
type: "set verb", useEffect(() => {
payload: props.verb, const VPSFromUrl = getVPSFromUrl();
}); if (VPSFromUrl) {
// eslint-disable-next-line setMode("phrases");
}, [props.verb]); adjustVps({
useEffect(() => { type: "load vps",
if (props.loaded) { payload: VPSFromUrl,
adjustVps({ });
type: "load vps",
payload: props.loaded,
});
}
// eslint-disable-next-line
}, [props.loaded]);
useEffect(() => {
const VPSFromUrl = getVPSFromUrl();
if (VPSFromUrl) {
setMode("phrases");
adjustVps({
type: "load vps",
payload: VPSFromUrl
});
}
// eslint-disable-next-line
}, []);
function handleSubjObjSwap() {
adjustVps({ type: "swap subj/obj" });
} }
function quizLock<T>(f: T) { // eslint-disable-next-line
if (mode === "quiz") { }, []);
return () => { function handleSubjObjSwap() {
flashMessage("to adjust this, get out of quiz mode"); adjustVps({ type: "swap subj/obj" });
return null; }
}; function quizLock<T>(f: T) {
} if (mode === "quiz") {
return f; return () => {
flashMessage("to adjust this, get out of quiz mode");
return null;
};
} }
function handleSetForm(form: T.FormVersion) { return f;
adjustVps({ }
type: "set form", function handleSetForm(form: T.FormVersion) {
payload: form, adjustVps({
}); type: "set form",
} payload: form,
function flashClippedMessage(m: string) { });
setShowClipped(m); }
setTimeout(() => { function flashClippedMessage(m: string) {
setShowClipped(""); setShowClipped(m);
}, 1250); setTimeout(() => {
} setShowClipped("");
// for some crazy reason I can't get the URI share thing to encode and decode properly }, 1250);
function handleCopyShareLink() { }
const shareUrl = getShareUrl(vps); // for some crazy reason I can't get the URI share thing to encode and decode properly
navigator.clipboard.writeText(shareUrl); function handleCopyShareLink() {
flashClippedMessage("Copied phrase URL to clipboard"); const shareUrl = getShareUrl(vps);
} navigator.clipboard.writeText(shareUrl);
function handleCopyCode() { flashClippedMessage("Copied phrase URL to clipboard");
const code = getCode(vps); }
navigator.clipboard.writeText(code); function handleCopyCode() {
flashClippedMessage("Copied phrase code to clipboard"); const code = getCode(vps);
} navigator.clipboard.writeText(code);
const object = getObjectSelection(vps.blocks).selection; flashClippedMessage("Copied phrase code to clipboard");
const VPS = completeVPSelection(vps); }
const phraseIsComplete = !!VPS; const object = getObjectSelection(vps.blocks).selection;
return <div className="mt-3" style={{ maxWidth: "950px"}}> const VPS = completeVPSelection(vps);
<VerbPicker const phraseIsComplete = !!VPS;
vps={vps} return (
onChange={quizLock(adjustVps)} <div className="mt-3" style={{ maxWidth: "950px" }}>
opts={props.opts} <VerbPicker
handleLinkClick={props.handleLinkClick} 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>
<ButtonSelect
value={mode}
options={[
{ label: "Charts", value: "charts" },
{ label: "Phrases", value: "phrases" },
{ label: "Quiz", value: "quiz" },
]}
handleChange={setMode}
/>
<div className="d-flex flex-row">
<div
className="clickable mr-4"
onClick={mode === "phrases" ? handleCopyCode : undefined}
style={{ width: "1rem" }}
>
{mode === "phrases" && phraseIsComplete ? (
<i className="fas fa-code" />
) : (
""
)}
</div>
<div
className="clickable"
onClick={mode === "phrases" ? handleCopyShareLink : undefined}
style={{ width: "1rem" }}
>
{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 className="text-center my-2">
<button
onClick={handleSubjObjSwap}
className="btn btn-sm btn-light"
>
<i className="fas fa-exchange-alt mr-2" /> subj/obj
</button>
</div>
)}
{mode === "phrases" && (
<VPPicker
opts={props.opts}
entryFeeder={props.entryFeeder}
onChange={(payload) => adjustVps({ type: "load vps", payload })}
vps={vps}
/> />
{!props.onlyPhrases && <div className="mt-2 mb-3 d-flex flex-row justify-content-between align-items-center"> )}
<div style={{ width: "1rem" }}> {mode !== "phrases" && (
</div> <div className="my-2">
<ButtonSelect <TensePicker vps={vps} onChange={adjustVps} mode={mode} />
value={mode} </div>
options={[ )}
{ label: "Charts", value: "charts" }, {mode === "phrases" && (
{ label: "Phrases", value: "phrases" }, <VPDisplay VPS={vps} opts={props.opts} setForm={handleSetForm} />
{ label: "Quiz", value: "quiz" }, )}
]} {mode === "charts" && (
handleChange={setMode} <AllTensesDisplay
/> object={object}
<div className="d-flex flex-row"> VS={vps.verb}
<div opts={props.opts}
className="clickable mr-4" onChange={adjustVps}
onClick={mode === "phrases" ? handleCopyCode : undefined} />
style={{ width: "1rem" }} )}
> {mode === "quiz" && <VPExplorerQuiz opts={props.opts} vps={vps} />}
{(mode === "phrases" && phraseIsComplete) ? <i className="fas fa-code" /> : ""} {showClipped && (
</div> <div
<div className="alert alert-primary text-center"
className="clickable" role="alert"
onClick={mode === "phrases" ? handleCopyShareLink : undefined} style={{
style={{ width: "1rem" }}
>
{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 className="text-center my-2">
<button onClick={handleSubjObjSwap} className="btn btn-sm btn-light">
<i className="fas fa-exchange-alt mr-2" /> subj/obj
</button>
</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", position: "fixed",
top: "30%", top: "30%",
left: "50%", left: "50%",
transform: "translate(-50%, -50%)", transform: "translate(-50%, -50%)",
zIndex: 9999999999999, zIndex: 9999999999999,
}}> }}
{showClipped} >
</div>} {showClipped}
{alertMsg && <div className="alert alert-warning text-center" role="alert" style={{ </div>
)}
{alertMsg && (
<div
className="alert alert-warning text-center"
role="alert"
style={{
position: "fixed", position: "fixed",
top: "30%", top: "30%",
left: "50%", left: "50%",
transform: "translate(-50%, -50%)", transform: "translate(-50%, -50%)",
zIndex: 9999999999999, zIndex: 9999999999999,
}}> }}
{alertMsg} >
</div>} {alertMsg}
</div>
)}
</div> </div>
);
} }
export default VPExplorer; export default VPExplorer;
function getShareUrl(vps: T.VPSelectionState): string { function getShareUrl(vps: T.VPSelectionState): string {
const code = getCode(vps); const code = getCode(vps);
const encoded = LZString.compressToEncodedURIComponent(code); const encoded = LZString.compressToEncodedURIComponent(code);
const url = new URL(window.location.href); const url = new URL(window.location.href);
// need to delete or else you could just get a second param written after // need to delete or else you could just get a second param written after
// which gets ignored // which gets ignored
url.searchParams.delete(vpPhraseURLParam); url.searchParams.delete(vpPhraseURLParam);
url.searchParams.append(vpPhraseURLParam, encoded); url.searchParams.append(vpPhraseURLParam, encoded);
return url.toString(); return url.toString();
} }
function getCode(vps: T.VPSelectionState): string { function getCode(vps: T.VPSelectionState): string {
return JSON.stringify(vps); return JSON.stringify(vps);
} }
function getVPSFromUrl(): T.VPSelectionState | undefined { function getVPSFromUrl(): T.VPSelectionState | undefined {
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
const fromParams = params.get(vpPhraseURLParam); const fromParams = params.get(vpPhraseURLParam);
if (!fromParams) return; if (!fromParams) return;
const decoded = LZString.decompressFromEncodedURIComponent(fromParams); const decoded = LZString.decompressFromEncodedURIComponent(fromParams);
return JSON.parse(decoded) as T.VPSelectionState; return JSON.parse(decoded) as T.VPSelectionState;
} }

View File

@ -2,232 +2,435 @@ import NPPicker, { shrunkenBackground } from "../../src/np-picker/NPPicker";
import TensePicker from "./TensePicker"; import TensePicker from "./TensePicker";
import * as T from "../../../types"; import * as T from "../../../types";
import { import {
// useEffect, // useEffect,
useRef, useRef,
useState, useState,
} from "react"; } from "react";
import { getKingAndServant, renderVP } from "../../../lib/src/phrase-building/render-vp"; import {
import { completeVPSelection, isPastTense } from "../../../lib/src/phrase-building/vp-tools"; getKingAndServant,
import VPExplorerExplanationModal, { roleIcon } from "./VPExplorerExplanationModal"; 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 APPicker from "../../src/ap-picker/APPicker";
// import autoAnimate from "@formkit/auto-animate"; // import autoAnimate from "@formkit/auto-animate";
import { import {
getObjectSelection, getObjectSelection,
getSubjectSelection, getSubjectSelection,
includesShrunkenServant, includesShrunkenServant,
isNoObject, isNoObject,
} from "../../../lib/src/phrase-building/blocks-utils"; } from "../../../lib/src/phrase-building/blocks-utils";
import ComplementPicker from "../ComplementPicker"; 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 }: { function VPPicker({
opts: T.TextOptions, opts,
vps: T.VPSelectionState, vps,
onChange: (vps: T.VPSelectionState) => void, onChange,
entryFeeder: T.EntryFeeder, entryFeeder,
}: {
opts: T.TextOptions;
vps: T.VPSelectionState;
onChange: (vps: T.VPSelectionState) => void;
entryFeeder: T.EntryFeeder;
}) { }) {
const parent = useRef<HTMLDivElement>(null); const parent = useRef<HTMLDivElement>(null);
// useEffect(() => { // useEffect(() => {
// parent.current && autoAnimate(parent.current); // parent.current && autoAnimate(parent.current);
// }, [parent]); // }, [parent]);
const [showingExplanation, setShowingExplanation] = useState<{ role: "servant" | "king", item: "subject" | "object" } | false>(false); const [showingExplanation, setShowingExplanation] =
function adjustVps(action: VpsReducerAction) { useState<{ role: "servant" | "king"; item: "subject" | "object" } | false>(
onChange(vpsReducer(vps, action)); false
}
function handleSubjectChange(subject: T.NPSelection | undefined, skipPronounConflictCheck?: boolean) {
adjustVps({
type: "set subject",
payload: { subject, skipPronounConflictCheck },
});
}
function handleObjectChange(object: T.NPSelection | undefined) {
adjustVps({
type: "set object",
payload: object,
});
}
const object = getObjectSelection(vps.blocks).selection;
const subject = getSubjectSelection(vps.blocks).selection;
const VPS = completeVPSelection(vps);
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 roles = getKingAndServant(
isPast,
vps.verb.transitivity !== "intransitive",
); );
return <div> function adjustVps(action: VpsReducerAction) {
<div className="clickable h5" onClick={() => adjustVps({ type: "insert new AP" })}>+ AP</div> onChange(vpsReducer(vps, action));
<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) => { function handleSubjectChange(
if (isNoObject(block)) return null; subject: T.NPSelection | undefined,
return <div className="my-2 card block-card p-1 mx-1" key={key} style={{ skipPronounConflictCheck?: boolean
background: (servantIsShrunk && ( ) {
(roles.servant === "subject" && block?.type === "subjectSelection") adjustVps({
|| type: "set subject",
(roles.servant === "object" && block?.type === "objectSelection") payload: { subject, skipPronounConflictCheck },
)) ? shrunkenBackground : "inherit", });
}}> }
<div className="d-flex flex-row justify-content-between mb-1" style={{ height: "1rem" }}> function handleObjectChange(object: T.NPSelection | undefined) {
{(i > 0 && !isNoObject(blocks[i - 1].block)) ? <div adjustVps({
className="small clickable ml-1" type: "set object",
onClick={() => adjustVps({ type: "shift block", payload: { index: i, direction: "back" }})} payload: object,
> });
<i className="fas fa-chevron-left" /> }
</div> : <div/>} const object = getObjectSelection(vps.blocks).selection;
{(i < vps.blocks.length - 1 && !isNoObject(blocks[i + 1].block)) ? <div const subject = getSubjectSelection(vps.blocks).selection;
className="small clickable mr-1" const VPS = completeVPSelection(vps);
onClick={() => adjustVps({ type: "shift block", payload: { index: i, direction: "forward" }})} const phraseIsComplete = !!VPS;
> const rendered = VPS ? renderVP(VPS) : undefined;
<i className="fas fa-chevron-right" /> const servantIsShrunk = includesShrunkenServant(rendered?.kids);
</div> : <div/>} const isPast = isPastTense(
</div> vps.verb.tenseCategory === "perfect"
{(!block || block.type === "AP") ? vps.verb.perfectTense
? <APPicker : vps.verb.verbTense
phraseIsComplete={phraseIsComplete} );
heading="AP" const roles = getKingAndServant(
entryFeeder={entryFeeder} isPast,
AP={block} vps.verb.transitivity !== "intransitive"
opts={opts} );
onChange={AP => adjustVps({ type: "set AP", payload: { index: i, AP } })} return (
onRemove={() => adjustVps({ type: "remove AP", payload: i })} <div>
/> <div
: (block?.type === "subjectSelection") className="clickable h5"
? <NPPicker onClick={() => adjustVps({ type: "insert new AP" })}
phraseIsComplete={phraseIsComplete} >
heading={roles.king === "subject" + AP
? <div className="h5 text-center"> </div>
Subj. <span onClick={() => setShowingExplanation({ role: "king", item: "subject" })}>{roleIcon.king}</span> <div
{(rendered && rendered.whatsAdjustable !== "servant") && ref={parent}
<KingRemover className="d-flex flex-row justify-content-around flex-wrap"
onChange={() => adjustVps({ type: "toggle king remove" })} style={{ marginLeft: "-0.5rem", marginRight: "-0.5rem" }}
showKing={!VPS?.form.removeKing} >
/> {vps.blocks.map(({ block, key }, i, blocks) => {
} if (isNoObject(block)) return null;
</div> return (
: <div className="h5 text-center"> <div
Subj. className="my-2 card block-card p-1 mx-1"
{` `} key={key}
<span className="clickable" onClick={() => setShowingExplanation({ role: "servant", item: "subject" })}>{roleIcon.servant}</span> style={{
{` `} background:
{(rendered && rendered.whatsAdjustable !== "king") && servantIsShrunk &&
<ServantShrinker ((roles.servant === "subject" &&
shrunk={servantIsShrunk} block?.type === "subjectSelection") ||
onClick={() => adjustVps({ type: "toggle servant shrink" })} (roles.servant === "object" &&
/> block?.type === "objectSelection"))
} ? shrunkenBackground
</div>} : "inherit",
entryFeeder={entryFeeder} }}
np={block.selection} >
counterPart={vps.verb ? object : undefined} <div
role={(isPast && vps.verb.transitivity !== "intransitive") className="d-flex flex-row justify-content-between mb-1"
? "ergative" style={{ height: "1rem" }}
: "subject" >
} {i > 0 &&
onChange={handleSubjectChange} !isNoObject(blocks[i - 1].block) &&
opts={opts} !isGenStatCompNoun(block) ? (
isShrunk={(servantIsShrunk && roles.servant === "subject")} <div
isRemoved={roles.king === "subject" && VPS?.form.removeKing} className="small clickable ml-1"
/> onClick={() =>
: (vps.verb && block?.type === "objectSelection" && block.selection !== "none") adjustVps({
? <div className="my-2" style={{ background: (servantIsShrunk && roles.servant === "object") ? shrunkenBackground : "inherit" }}> type: "shift block",
{(typeof block.selection === "number") payload: { index: i, direction: "back" },
? <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
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") &&
<KingRemover
onChange={() => adjustVps({ type: "toggle king remove" })}
showKing={!VPS?.form.removeKing}
/>
}
</div>
: <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" })} />
}
</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}
/>}
</div>
: null}
</div>;
})}
{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"
? undefined
: vps.externalComplement as T.ComplementSelection // TODO: just typescript being dumb? - looks like it
} }
onChange={payload => adjustVps({ type: "set externalComplement", payload })} >
opts={opts} <i className="fas fa-chevron-left" />
entryFeeder={entryFeeder} </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" },
})
}
>
<i className="fas fa-chevron-right" />
</div>
) : (
<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 } })
}
onRemove={() => adjustVps({ type: "remove AP", payload: i })}
/> />
</div>} ) : block?.type === "subjectSelection" ? (
<div className="my-2"> <NPPicker
<TensePicker phraseIsComplete={phraseIsComplete}
vps={vps} heading={
onChange={adjustVps} roles.king === "subject" ? (
mode="phrases" <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" })
}
showKing={!VPS?.form.removeKing}
/>
)}
</div>
) : (
<div className="h5 text-center">
Subj.
{` `}
<span
className="clickable"
onClick={() =>
setShowingExplanation({
role: "servant",
item: "subject",
})
}
>
{roleIcon.servant}
</span>
{` `}
{rendered && rendered.whatsAdjustable !== "king" && (
<ServantShrinker
shrunk={servantIsShrunk}
onClick={() =>
adjustVps({ type: "toggle servant shrink" })
}
/>
)}
</div>
)
}
entryFeeder={entryFeeder}
np={block.selection}
counterPart={vps.verb ? object : undefined}
role={
isPast && vps.verb.transitivity !== "intransitive"
? "ergative"
: "subject"
}
onChange={handleSubjectChange}
opts={opts}
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>
)}
<div className="text-muted text-center">
<div className="mt-3 mb-1">Unspoken</div>
<div>3rd Pers. Masc. Plur.</div>
</div>
</div>
) : (
<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" && (
<KingRemover
onChange={() =>
adjustVps({ type: "toggle king remove" })
}
showKing={!VPS?.form.removeKing}
/>
)}
</div>
) : (
<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" })
}
/>
)}
</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
}
/>
)}
</div>
) : null}
</div> </div>
);
})}
{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"
? undefined
: (vps.externalComplement as T.ComplementSelection) // TODO: just typescript being dumb? - looks like it
}
onChange={(payload) =>
adjustVps({ type: "set externalComplement", payload })
}
opts={opts}
entryFeeder={entryFeeder}
/>
</div>
)}
<div className="my-2">
<TensePicker vps={vps} onChange={adjustVps} mode="phrases" />
</div> </div>
<VPExplorerExplanationModal </div>
showing={showingExplanation} <VPExplorerExplanationModal
setShowing={setShowingExplanation} showing={showingExplanation}
/> setShowing={setShowingExplanation}
</div>; />
</div>
);
} }
function ServantShrinker({ shrunk, onClick }: { function ServantShrinker({
shrunk: boolean; shrunk,
onClick: () => void; onClick,
}: {
shrunk: boolean;
onClick: () => void;
}) { }) {
return <span className="mx-2 clickable" onClick={onClick}> return (
{!shrunk ? "🪄" : "👶"} <span className="mx-2 clickable" onClick={onClick}>
</span>; {!shrunk ? "🪄" : "👶"}
</span>
);
} }
function KingRemover({ showKing, onChange }: { function KingRemover({
showKing: boolean; showKing,
onChange: () => void; onChange,
}: {
showKing: boolean;
onChange: () => void;
}) { }) {
return <span className="form-check form-check-inline ml-3"> return (
<input <span className="form-check form-check-inline ml-3">
checked={showKing} <input
onChange={onChange} checked={showKing}
className="form-check-input" onChange={onChange}
type="checkbox" className="form-check-input"
id="showKingCheck" type="checkbox"
/> id="showKingCheck"
</span>; />
</span>
);
} }
export default VPPicker; 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,141 +1,213 @@
import * as T from "../../../types"; import * as T from "../../../types";
import ButtonSelect from "../ButtonSelect"; import ButtonSelect from "../ButtonSelect";
import { RootsAndStems } from "../verb-info/VerbInfo"; 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 Hider from "../Hider";
import useStickyState from "../useStickyState"; import useStickyState from "../useStickyState";
import CompoundDisplay from "./CompoundDisplay"; import CompoundDisplay from "./CompoundDisplay";
import { import { VpsReducerAction } from "../../../lib/src/phrase-building/vps-reducer";
VpsReducerAction
} from "../../../lib/src/phrase-building/vps-reducer";
import { ensureNonComboVerbInfo } from "../../../lib/src/misc-helpers"; import { ensureNonComboVerbInfo } from "../../../lib/src/misc-helpers";
// TODO: dark on past tense selecitons // TODO: dark on past tense selecitons
function VerbPicker(props: { function VerbPicker(props: {
vps: T.VPSelectionState, vps: T.VPSelectionState;
onChange: (a: VpsReducerAction) => void, onChange: (a: VpsReducerAction) => void;
opts: T.TextOptions, opts: T.TextOptions;
handleLinkClick: ((ts: number) => void) | "none", handleLinkClick: ((ts: number) => void) | "none";
}) { }) {
const [showRootsAndStems, setShowRootsAndStems] = useStickyState<boolean>(false, "showRootsAndStems"); const [showRootsAndStems, setShowRootsAndStems] = useStickyState<boolean>(
const infoRaw = props.vps.verb ? getVerbInfo(props.vps.verb.verb.entry, props.vps.verb.verb.complement) : undefined; false,
const info = (!infoRaw || !props.vps.verb) "showRootsAndStems"
? undefined );
: ("stative" in infoRaw) const infoRaw = props.vps.verb
? infoRaw[props.vps.verb.isCompound === "stative" ? "stative" : "dynamic"] ? getVerbInfo(props.vps.verb.verb.entry, props.vps.verb.verb.complement)
: ("transitive" in infoRaw) : undefined;
? infoRaw[props.vps.verb.transitivity === "grammatically transitive" ? "grammaticallyTransitive" : "transitive"] const info =
: infoRaw; !infoRaw || !props.vps.verb
if (info && ("stative" in info || "transitive" in info)) { ? undefined
return <div>ERROR: Verb version should be select first</div>; : "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>;
}
function onVoiceSelect(value: "active" | "passive") {
props.onChange({
type: "set voice",
payload: value,
});
}
function notInstransitive(
t: "transitive" | "intransitive" | "grammatically transitive"
): "transitive" | "grammatically transitive" {
return t === "intransitive" ? "transitive" : t;
}
function handleChangeTransitivity(
payload: "transitive" | "grammatically transitive"
) {
props.onChange({
type: "set transitivity",
payload,
});
}
function handleChangeStatDyn(payload: "stative" | "dynamic") {
props.onChange({
type: "set statDyn",
payload,
});
}
const passiveRootsAndStems =
info && props.vps.verb.voice === "passive"
? getPassiveRootsAndStems(info)
: undefined;
const abilityRootsAndStems = (() => {
try {
return info && props.vps.verb.tenseCategory === "modal"
? getAbilityRootsAndStems(info)
: undefined;
} catch (e) {
console.log("error making ability roots and stems", e);
return undefined;
} }
function onVoiceSelect(value: "active" | "passive") { })();
props.onChange({ return (
type: "set voice", <div className="mb-3">
payload: value, {info && (
}); <CompoundDisplay
} info={info}
function notInstransitive(t: "transitive" | "intransitive" | "grammatically transitive"): "transitive" | "grammatically transitive" { opts={props.opts}
return t === "intransitive" ? "transitive" : t; handleLinkClick={props.handleLinkClick}
} />
function handleChangeTransitivity(payload: "transitive" | "grammatically transitive") { )}
props.onChange({ {info && (
type: "set transitivity", <div className="mt-3 mb-1 text-center">
payload, <Hider
}); showing={showRootsAndStems}
} label={`🌳 ${
function handleChangeStatDyn(payload: "stative" | "dynamic") { passiveRootsAndStems
props.onChange({ ? "Passive"
type: "set statDyn", : abilityRootsAndStems
payload, ? "Ability"
}); : ""
} } Roots and Stems${
const passiveRootsAndStems = (info && props.vps.verb.voice === "passive") ? getPassiveRootsAndStems(info) : undefined; info.type === "dynamic compound" ? " for Aux. Verb" : ""
const abilityRootsAndStems = (() => { }`}
try { handleChange={() => setShowRootsAndStems((p) => !p)}
return (info && props.vps.verb.tenseCategory === "modal") ? getAbilityRootsAndStems(info) : undefined; hLevel={5}
} catch (e) { >
console.log("error making ability roots and stems", e); <RootsAndStems
return undefined; textOptions={props.opts}
} info={
})(); passiveRootsAndStems
return <div className="mb-3"> ? passiveRootsAndStems
{info && <CompoundDisplay : abilityRootsAndStems
info={info} ? abilityRootsAndStems
opts={props.opts} : info.type === "dynamic compound"
handleLinkClick={props.handleLinkClick} ? ensureNonComboVerbInfo(getVerbInfo(info.auxVerb))
/>} : info
{info && <div className="mt-3 mb-1 text-center"> }
<Hider />
showing={showRootsAndStems} </Hider>
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
? passiveRootsAndStems
: abilityRootsAndStems
? abilityRootsAndStems
: info.type === "dynamic compound"
? ensureNonComboVerbInfo(getVerbInfo(info.auxVerb))
: 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">
<ButtonSelect
small
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">
<ButtonSelect
small
value={props.vps.verb.voice}
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">
<ButtonSelect
small
options={[{
label: "stative",
value: "stative",
}, {
label: "dynamic",
value: "dynamic",
}]}
value={props.vps.verb.isCompound ? props.vps.verb.isCompound : "stative"}
handleChange={handleChangeStatDyn}
/>
</div>}
</div> </div>
</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={[
{
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">
<ButtonSelect
small
value={props.vps.verb.voice}
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">
<ButtonSelect
small
options={[
{
label:
infoRaw?.type === "dynamic or generative stative compound"
? "gen. stative"
: "stative",
value: "stative",
},
{
label: "dynamic",
value: "dynamic",
},
]}
value={
props.vps.verb.isCompound === "generative stative"
? "stative"
: props.vps.verb.isCompound
? props.vps.verb.isCompound
: "stative"
}
handleChange={handleChangeStatDyn}
/>
</div>
)}
</div>
</div>
);
} }
export default VerbPicker;
export default VerbPicker;

View File

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

View File

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

View File

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

View File

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

View File

@ -2,289 +2,403 @@ import * as T from "../../../types";
import { getLength } from "../p-text-helpers"; import { getLength } from "../p-text-helpers";
export function makeBlock(block: T.Block["block"], key?: number): T.Block { export function makeBlock(block: T.Block["block"], key?: number): T.Block {
return { return {
key: key === undefined ? Math.random() : key, key: key === undefined ? Math.random() : key,
block, block,
}; };
} }
export function makeKid(kid: T.Kid["kid"], key?: number): T.Kid { export function makeKid(kid: T.Kid["kid"], key?: number): T.Kid {
return { return {
key: key === undefined ? Math.random() : key, key: key === undefined ? Math.random() : key,
kid, kid,
}; };
} }
export function getSubjectSelection(blocks: T.EPSBlockComplete[] | T.VPSBlockComplete[]): T.SubjectSelectionComplete; export function getSubjectSelection(
export function getSubjectSelection(blocks: T.EPSBlock[] | T.VPSBlock[]): T.SubjectSelection; blocks: T.EPSBlockComplete[] | T.VPSBlockComplete[]
export function getSubjectSelection(blocks: T.EPSBlock[] | T.EPSBlockComplete[] | T.VPSBlockComplete[] | T.VPSBlock[]): T.SubjectSelection | T.SubjectSelectionComplete { ): T.SubjectSelectionComplete;
const b = blocks.find(f => f.block?.type === "subjectSelection"); export function getSubjectSelection(
if (!b || !b.block || b.block.type !== "subjectSelection") { blocks: T.EPSBlock[] | T.VPSBlock[]
throw new Error("subjectSelection not found in blocks"); ): T.SubjectSelection;
} export function getSubjectSelection(
return b.block; 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 { export function getComplementFromBlocks(
const b = blocks[0].find(f => f.block.type === "complement"); blocks: T.Block[][]
return b?.block as T.Rendered<T.ComplementSelection> | T.UnselectedComplementSelection | undefined; ):
| 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> { export function getSubjectSelectionFromBlocks(
const b = blocks[0].find(f => f.block.type === "subjectSelection"); blocks: T.Block[][]
if (!b || b.block.type !== "subjectSelection") { ): T.Rendered<T.SubjectSelectionComplete> {
throw new Error("subjectSelection not found in blocks"); const b = blocks[0].find((f) => f.block.type === "subjectSelection");
} if (!b || b.block.type !== "subjectSelection") {
return b.block; throw new Error("subjectSelection not found in blocks");
}
return b.block;
} }
export function getObjectSelectionFromBlocks(blocks: T.Block[][]): T.Rendered<T.ObjectSelectionComplete> { export function getObjectSelectionFromBlocks(
const b = blocks[0].find(f => f.block.type === "objectSelection"); blocks: T.Block[][]
if (!b || b.block.type !== "objectSelection") { ): T.Rendered<T.ObjectSelectionComplete> {
throw new Error("objectSelection not found in blocks"); const b = blocks[0].find((f) => f.block.type === "objectSelection");
} if (!b || b.block.type !== "objectSelection") {
return b.block; throw new Error("objectSelection not found in blocks");
}
return b.block;
} }
export function includesShrunkenServant(kids?: T.Kid[]): boolean { export function includesShrunkenServant(kids?: T.Kid[]): boolean {
if (!kids) return false; if (!kids) return false;
return kids.some(k => ( return kids.some(
k.kid.type === "mini-pronoun" && k.kid.source === "servant" (k) => k.kid.type === "mini-pronoun" && k.kid.source === "servant"
)); );
} }
export function getPredicateSelectionFromBlocks(blocks: T.Block[][]): T.Rendered<T.PredicateSelectionComplete> { export function getPredicateSelectionFromBlocks(
const b = blocks[0].find(f => f.block.type === "predicateSelection"); blocks: T.Block[][]
if (!b || b.block.type !== "predicateSelection") { ): T.Rendered<T.PredicateSelectionComplete> {
throw new Error("predicateSelection not found in blocks"); const b = blocks[0].find((f) => f.block.type === "predicateSelection");
} if (!b || b.block.type !== "predicateSelection") {
return b.block; throw new Error("predicateSelection not found in blocks");
}
return b.block;
} }
export function getAPsFromBlocks(blocks: T.Block[][]): T.Rendered<T.APSelection>[] { export function getAPsFromBlocks(
return blocks[0].filter(b => b.block.type === "AP").map(b => b.block) as T.Rendered<T.APSelection>[]; 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.ObjectSelection;
export function getObjectSelection(blocks: T.VPSBlock[] | T.VPSBlockComplete[]): T.ObjectSelection | T.ObjectSelectionComplete { export function getObjectSelection(
const b = blocks.find(f => f.block?.type === "objectSelection"); blocks: T.VPSBlock[] | T.VPSBlockComplete[]
if (!b || !b.block || b.block.type !== "objectSelection") { ): T.ObjectSelection | T.ObjectSelectionComplete {
throw new Error("objectSelection not found in blocks"); const b = blocks.find((f) => f.block?.type === "objectSelection");
} if (!b || !b.block || b.block.type !== "objectSelection") {
return b.block; throw new Error("objectSelection not found in blocks");
}
return b.block;
} }
export function makeEPSBlocks(): T.EPSBlock[] { export function makeEPSBlocks(): T.EPSBlock[] {
return [ return [
{ {
key: Math.random(), key: Math.random(),
block: { block: {
type: "subjectSelection",
selection: 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 {
if (!selection) {
return {
type: "subjectSelection",
selection: undefined,
};
}
if (selection.type === "subjectSelection") {
return selection;
}
if (selection.type === "NP") {
return {
type: "subjectSelection",
selection,
};
}
return {
type: "subjectSelection", type: "subjectSelection",
selection: { selection: undefined,
type: "NP", },
selection, },
} ];
};
} }
export function makeObjectSelection(selection: T.ObjectSelection | T.ObjectNP | T.NPSelection | T.NPSelection["selection"] | undefined): T.ObjectSelection { export function makeAPBlock(): { key: number; block: undefined } {
if (!selection) { return {
return { key: Math.random(),
type: "objectSelection", block: undefined,
selection: undefined, };
} }
}
if (typeof selection !== "object") { export function makeSubjectSelection(
return { selection:
type: "objectSelection", | T.SubjectSelection
selection, | T.NPSelection
}; | T.NPSelection["selection"]
} | undefined
if (selection.type === "objectSelection") { ): T.SubjectSelection {
return selection; if (!selection) {
}
if (selection.type === "NP") {
return {
type: "objectSelection",
selection,
};
}
return { return {
type: "objectSelection", type: "subjectSelection",
selection: { selection: undefined,
type: "NP",
selection,
},
}; };
}
if (selection.type === "subjectSelection") {
return selection;
}
if (selection.type === "NP") {
return {
type: "subjectSelection",
selection,
};
}
return {
type: "subjectSelection",
selection: {
type: "NP",
selection,
},
};
} }
export function EPSBlocksAreComplete(blocks: T.EPSBlock[]): blocks is T.EPSBlockComplete[] { export function makeObjectSelection(
if (blocks.some(block => block.block === undefined)) { selection:
return false; | T.ObjectSelection
} | T.ObjectNP
const subject = getSubjectSelection(blocks); | T.NPSelection
return !!subject.selection; | T.NPSelection["selection"]
| undefined
): T.ObjectSelection {
if (!selection) {
return {
type: "objectSelection",
selection: undefined,
};
}
if (typeof selection !== "object") {
return {
type: "objectSelection",
selection,
};
}
if (selection.type === "objectSelection") {
return selection;
}
if (selection.type === "NP") {
return {
type: "objectSelection",
selection,
};
}
return {
type: "objectSelection",
selection: {
type: "NP",
selection,
},
};
} }
export function VPSBlocksAreComplete(blocks: T.VPSBlock[]): blocks is T.VPSBlockComplete[] { export function EPSBlocksAreComplete(
if (blocks.some(block => block.block === undefined)) { blocks: T.EPSBlock[]
return false; ): blocks is T.EPSBlockComplete[] {
} if (blocks.some((block) => block.block === undefined)) {
const subject = getSubjectSelection(blocks); return false;
if (!subject.selection) return false; }
const object = getObjectSelection(blocks); const subject = getSubjectSelection(blocks);
if (!object.selection) return false; return !!subject.selection;
return true;
} }
export function adjustSubjectSelection(blocks: T.EPSBlock[], subject: T.SubjectSelection | T.NPSelection | undefined): T.EPSBlock[]; export function VPSBlocksAreComplete(
export function adjustSubjectSelection(blocks: T.VPSBlock[], subject: T.SubjectSelection | T.NPSelection | undefined): T.VPSBlock[]; blocks: T.VPSBlock[]
export function adjustSubjectSelection(blocks: T.VPSBlock[] | T.EPSBlock[], subject: T.SubjectSelection | T.NPSelection | undefined): T.VPSBlock[] | T.EPSBlock[] { ): blocks is T.VPSBlockComplete[] {
const nb = [...blocks]; if (blocks.some((block) => block.block === undefined)) {
const i = nb.findIndex(b => b.block && b.block.type === "subjectSelection"); return false;
if (i === -1) { }
throw new Error("couldn't find subjectSelection to modify"); const subject = getSubjectSelection(blocks);
} if (!subject.selection) return false;
nb[i].block = subject?.type === "subjectSelection" ? subject : makeSubjectSelection(subject); const object = getObjectSelection(blocks);
return nb; if (!object.selection) return false;
return true;
} }
export function adjustObjectSelection(blocks: Readonly<T.VPSBlock[]>, object: T.ObjectSelectionComplete | T.NPSelection | T.VerbObject | T.ObjectSelectionComplete): T.VPSBlockComplete[]; export function adjustSubjectSelection(
export function adjustObjectSelection(blocks: Readonly<T.VPSBlock[]>, object: T.ObjectSelection | T.NPSelection | T.VerbObject | T.ObjectSelection | undefined): T.EPSBlock[]; blocks: T.EPSBlock[],
export function adjustObjectSelection(blocks: Readonly<T.VPSBlock[]>, object: T.ObjectSelection | T.ObjectSelectionComplete | T.VerbObject | T.NPSelection | undefined): T.VPSBlock[] | T.VPSBlockComplete[] { subject: T.SubjectSelection | T.NPSelection | undefined
const nb = [...blocks]; ): T.EPSBlock[];
const i = nb.findIndex(b => b.block && b.block.type === "objectSelection"); export function adjustSubjectSelection(
if (i === -1) { blocks: T.VPSBlock[],
throw new Error("couldn't find objectSelection to modify"); subject: T.SubjectSelection | T.NPSelection | undefined
} ): T.VPSBlock[];
nb[i].block = typeof object === "object" && object?.type === "objectSelection" export function adjustSubjectSelection(
? object blocks: T.VPSBlock[] | T.EPSBlock[],
: makeObjectSelection(object); subject: T.SubjectSelection | T.NPSelection | undefined
return nb; ): T.VPSBlock[] | T.EPSBlock[] {
const nb = [...blocks];
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);
return nb;
} }
export function shiftBlock<B extends T.VPSBlock[] | T.EPSBlock[]>(blocks: B, index: number, direction: "back" | "forward"): B { export function adjustObjectSelection(
const newIndex = index + (direction === "forward" blocks: Readonly<T.VPSBlock[]>,
? 1 // (isNoObject(blocks[index + 1].block) ? 2 : 1) object:
: -1 // (isNoObject(blocks[index - 1].block) ? -2 : -2) | T.ObjectSelectionComplete
); | T.NPSelection
return arrayMove(blocks, index, newIndex) as B; | 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");
if (i === -1) {
throw new Error("couldn't find objectSelection to modify");
}
nb[i].block =
typeof object === "object" && object?.type === "objectSelection"
? object
: makeObjectSelection(object);
return nb;
} }
export function insertNewAP<B extends T.VPSBlock[] | T.EPSBlock[]>(blocks: B): B { export function moveObjectToEnd(
return [makeAPBlock(), ...blocks] as B; 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 setAP<B extends T.VPSBlock[] | T.EPSBlock[]>(blocks: B, index: number, AP: T.APSelection | undefined): B { export function shiftBlock<B extends T.VPSBlock[] | T.EPSBlock[]>(
const nBlocks = [...blocks] as B; blocks: B,
nBlocks[index].block = AP; index: number,
return nBlocks; 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 removeAP<B extends T.VPSBlock[] | T.EPSBlock[]>(blocks: B, index: number): B { export function insertNewAP<B extends T.VPSBlock[] | T.EPSBlock[]>(
const nBlocks = [...blocks] as B; blocks: B
nBlocks.splice(index, 1); ): B {
return nBlocks; return [makeAPBlock(), ...blocks] as B;
} }
export function isNoObject(b: T.VPSBlock["block"] | T.EPSBlock["block"]): b is { type: "objectSelection", selection: "none" } { export function setAP<B extends T.VPSBlock[] | T.EPSBlock[]>(
return !!( blocks: B,
b index: number,
&& AP: T.APSelection | undefined
(b.type === "objectSelection" && b.selection === "none") ): B {
); const nBlocks = [...blocks] as B;
nBlocks[index].block = AP;
return nBlocks;
} }
export function specifyEquativeLength(blocksWVars: T.Block[][], length: "long" | "short"): T.Block[][] { export function removeAP<B extends T.VPSBlock[] | T.EPSBlock[]>(
function specify(blocks: T.Block[]): T.Block[] { blocks: B,
const i = blocks.findIndex(b => b.block.type === "equative"); index: number
if (i === -1) throw new Error("equative block not found in EPRendered"); ): B {
const eq = blocks[i]; const nBlocks = [...blocks] as B;
if (eq.block.type !== "equative") throw new Error("error searching for equative block"); nBlocks.splice(index, 1);
const adjusted = [...blocks]; return nBlocks;
adjusted[i] = { }
...eq,
block: { export function isNoObject(
...eq.block, b: T.VPSBlock["block"] | T.EPSBlock["block"]
equative: { ): b is { type: "objectSelection"; selection: "none" } {
...eq.block.equative, return !!(b && b.type === "objectSelection" && b.selection === "none");
ps: getLength(eq.block.equative.ps, length), }
},
}, export function specifyEquativeLength(
}; blocksWVars: T.Block[][],
return adjusted; length: "long" | "short"
} ): T.Block[][] {
return blocksWVars.map(specify); function specify(blocks: T.Block[]): T.Block[] {
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");
const adjusted = [...blocks];
adjusted[i] = {
...eq,
block: {
...eq.block,
equative: {
...eq.block.equative,
ps: getLength(eq.block.equative.ps, length),
},
},
};
return adjusted;
}
return blocksWVars.map(specify);
} }
export function isRenderedVerbB({ block }: T.Block): boolean { export function isRenderedVerbB({ block }: T.Block): boolean {
if (block.type === "equative") { if (block.type === "equative") {
return true; return true;
} }
if (block.type === "VB") { if (block.type === "VB") {
return true; return true;
} }
if (block.type === "PH") { if (block.type === "PH") {
return true; return true;
} }
if (block.type === "NComp") { if (block.type === "NComp") {
return true; return true;
} }
if (block.type === "welded") { if (block.type === "welded") {
return true; return true;
} }
if (block.type === "complement") { if (block.type === "complement") {
return true; return true;
} }
return false return false;
} }
export function hasEquativeWithLengths(blocks: T.Block[][]): boolean { 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) 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")
return "long" in equative.block.equative.ps; 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[] { function arrayMove<X>(ar: X[], old_index: number, new_index: number): X[] {
const arr = [...ar]; const arr = [...ar];
const new_i = (new_index >= arr.length) const new_i =
? (arr.length - 1) new_index >= arr.length ? arr.length - 1 : new_index < 0 ? 0 : new_index;
: (new_index < 0) arr.splice(new_i, 0, arr.splice(old_index, 1)[0]);
? 0 return arr;
: new_index; }
arr.splice(new_i, 0, arr.splice(old_index, 1)[0]);
return arr;
};

View File

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

View File

@ -1,52 +1,63 @@
import { import {
isPluralNounEntry, isPluralNounEntry,
isMascNounEntry, isMascNounEntry,
isUnisexNounEntry, isUnisexNounEntry,
} from "../type-predicates"; } from "../type-predicates";
import * as T from "../../../types"; import * as T from "../../../types";
export function makeAdverbSelection(entry: T.AdverbEntry): T.AdverbSelection { export function makeAdverbSelection(entry: T.AdverbEntry): T.AdverbSelection {
return { return {
type: "adverb", type: "adverb",
entry: entry, entry: entry,
}; };
} }
export function makeLocativeAdverbSelection(entry: T.LocativeAdverbEntry): T.LocativeAdverbSelection { export function makeLocativeAdverbSelection(
return { entry: T.LocativeAdverbEntry
type: "loc. adv.", ): T.LocativeAdverbSelection {
entry: entry, return {
}; type: "loc. adv.",
entry: entry,
};
} }
export function makeAdjectiveSelection(entry: T.AdjectiveEntry): T.AdjectiveSelection { export function makeAdjectiveSelection(
return { entry: T.AdjectiveEntry
type: "adjective", ): T.AdjectiveSelection {
entry: entry, return {
sandwich: undefined, type: "adjective",
}; entry: entry,
sandwich: undefined,
};
} }
export function makeParticipleSelection(verb: T.VerbEntry): T.ParticipleSelection { export function makeParticipleSelection(
return { verb: T.VerbEntry
type: "participle", ): T.ParticipleSelection {
verb, return {
possesor: undefined, type: "participle",
}; verb,
possesor: undefined,
};
} }
export function makeNounSelection(entry: T.NounEntry, old: T.NounSelection | undefined, dynamicComplement?: true): T.NounSelection { export function makeNounSelection(
const number = isPluralNounEntry(entry) ? "plural" : "singular"; entry: T.NounEntry,
return { old: T.NounSelection | undefined,
type: "noun", complementType?: "dynamic" | "generative stative"
entry, ): T.NounSelection {
gender: isMascNounEntry(entry) ? "masc" : "fem", const number = isPluralNounEntry(entry) ? "plural" : "singular";
genderCanChange: isUnisexNounEntry(entry), return {
number, type: "noun",
numberCanChange: number === "singular", entry,
adjectives: (!dynamicComplement && old) ? old.adjectives : [], gender: isMascNounEntry(entry) ? "masc" : "fem",
possesor: !dynamicComplement ? old?.possesor : undefined, genderCanChange: isUnisexNounEntry(entry),
dynamicComplement, number,
demonstrative: undefined, numberCanChange: number === "singular",
}; 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 * as T from "../../../types";
import { mapVerbRenderedOutput } from "../fmaps"; import { mapVerbRenderedOutput } from "../fp-ps";
import { removeAccents } from "../accent-helpers"; import { removeAccents } from "../accent-helpers";
import { getPersonFromNP, isPastTense } from "./vp-tools"; import { getPersonFromNP, isPastTense } from "./vp-tools";
import { isImperativeTense, isPattern4Entry } from "../type-predicates"; import { isImperativeTense, isPattern4Entry } from "../type-predicates";
@ -19,6 +19,7 @@ import {
getMiniPronounPs, getMiniPronounPs,
} from "./render-common"; } from "./render-common";
import { renderComplementSelection } from "./render-complement"; import { renderComplementSelection } from "./render-complement";
import { statVerb } from "../new-verb-engine/roots-and-stems";
export function renderVP(VP: T.VPSelectionComplete): T.VPRendered { export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
const subject = getSubjectSelection(VP.blocks).selection; const subject = getSubjectSelection(VP.blocks).selection;
@ -45,8 +46,18 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
king, king,
complementPerson, complementPerson,
}); });
// TODO: for dynamic -
const { vbs, hasBa } = renderVerb({ 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, tense: VP.verb.tense,
subject: subjectPerson, subject: subjectPerson,
object: objectPerson, object: objectPerson,
@ -128,7 +139,6 @@ export function insertNegative(
if (!negative) { if (!negative) {
return [blocks.flat().map(makeBlock)]; return [blocks.flat().map(makeBlock)];
} }
const blocksA = blocks.flat().map(makeBlock);
const blocksNoAccentA = mapVerbRenderedOutput(removeAccents, blocks) const blocksNoAccentA = mapVerbRenderedOutput(removeAccents, blocks)
.flat() .flat()
.map(makeBlock); .map(makeBlock);
@ -138,13 +148,13 @@ export function insertNegative(
// swapped ending with negative for ability and perfect verb forms // swapped ending with negative for ability and perfect verb forms
if (nonStandPerfectiveSplit) { if (nonStandPerfectiveSplit) {
return [ return [
insertFromEnd(swapEndingBlocks(blocksA), neg, 2), insertFromEnd(swapEndingBlocks(blocksNoAccentA), neg, 2),
insertFromEnd(swapEndingBlocks(blocksA, 2), neg, 3), insertFromEnd(swapEndingBlocks(blocksNoAccentA, 2), neg, 3),
insertFromEnd(blocksNoAccentA, neg, 1), insertFromEnd(blocksNoAccentA, neg, 1),
]; ];
} }
return [ return [
insertFromEnd(swapEndingBlocks(blocksA), neg, 2), insertFromEnd(swapEndingBlocks(blocksNoAccentA), neg, 2),
insertFromEnd(blocksNoAccentA, neg, 1), insertFromEnd(blocksNoAccentA, neg, 1),
]; ];
} }
@ -280,8 +290,9 @@ function whatsAdjustable(
VP: T.VPSelectionComplete VP: T.VPSelectionComplete
): "both" | "king" | "servant" { ): "both" | "king" | "servant" {
// TODO: intransitive dynamic compounds? // TODO: intransitive dynamic compounds?
return VP.verb.isCompound === "dynamic" && return VP.verb.isCompound === "dynamic" ||
VP.verb.transitivity === "transitive" (VP.verb.isCompound === "generative stative" &&
VP.verb.transitivity === "transitive")
? isPastTense(VP.verb.tense) ? isPastTense(VP.verb.tense)
? "servant" ? "servant"
: "king" : "king"

View File

@ -1,131 +1,186 @@
import { import { makeNounSelection } from "./make-selections";
makeNounSelection,
} from "./make-selections";
import * as T from "../../../types"; import * as T from "../../../types";
import { getVerbInfo } from "../verb-info"; import { getVerbInfo } from "../verb-info";
import { import {
adjustObjectSelection, adjustObjectSelection,
getObjectSelection, getObjectSelection,
getSubjectSelection, getSubjectSelection,
makeObjectSelection, makeObjectSelection,
makeSubjectSelection, makeSubjectSelection,
moveObjectToEnd,
} from "./blocks-utils"; } from "./blocks-utils";
export function makeVPSelectionState( export function makeVPSelectionState(
verb: T.VerbEntry, verb: T.VerbEntry,
os?: T.VPSelectionState, os?: T.VPSelectionState
): T.VPSelectionState { ): T.VPSelectionState {
const info = getVerbInfo(verb.entry, verb.complement); const info = getVerbInfo(verb.entry, verb.complement);
const subject = (os?.verb.voice === "passive" && info.type === "dynamic compound") const subject =
? makeNounSelection(info.objComplement.entry as T.NounEntry, undefined, true) os?.verb.voice === "passive" && info.type === "dynamic compound"
: (os?.blocks ? getSubjectSelection(os.blocks).selection : undefined); ? makeNounSelection(
function getTransObjFromos() { info.objComplement.entry as T.NounEntry,
const osObj = os ? getObjectSelection(os.blocks).selection : undefined; undefined,
if ( "dynamic"
!os || )
osObj === "none" || : os?.blocks
typeof osObj === "number" || ? getSubjectSelection(os.blocks).selection
os.verb.isCompound === "dynamic" || : undefined;
(osObj?.selection.type === "noun" && osObj.selection.dynamicComplement) function getTransObjFromos() {
) return undefined; const osObj = os ? getObjectSelection(os.blocks).selection : undefined;
return osObj; if (
} !os ||
const transitivity: T.Transitivity = "grammaticallyTransitive" in info osObj === "none" ||
? "transitive" typeof osObj === "number" ||
: info.transitivity; os.verb.isCompound === "dynamic" ||
const object = (transitivity === "grammatically transitive") (osObj?.selection.type === "noun" &&
? T.Person.ThirdPlurMale (osObj.selection.dynamicComplement ||
: (info.type === "dynamic compound" && os?.verb.voice !== "passive") osObj.selection.genStativeComplement))
? makeNounSelection(info.objComplement.entry as T.NounEntry, undefined, true) )
: (transitivity === "transitive" && os?.verb.voice !== "passive") return undefined;
? getTransObjFromos() return osObj;
: "none"; }
const isCompound = ("stative" in info || info.type === "stative compound") const transitivity: T.Transitivity =
? "stative" "grammaticallyTransitive" in info ? "transitive" : info.transitivity;
: info.type === "dynamic compound" const object =
? "dynamic" transitivity === "grammatically transitive"
: false; ? T.Person.ThirdPlurMale
// TODO: here and below in the changeStatDyn function ... allow for entries with complement : (info.type === "dynamic compound" ||
const dynAuxVerb: T.VerbEntry | undefined = isCompound !== "dynamic" info.type === "generative stative compound") &&
? undefined os?.verb.voice !== "passive"
: info.type === "dynamic compound" ? makeNounSelection(
? { entry: info.auxVerb } as T.VerbEntry info.objComplement.entry as T.NounEntry,
: "dynamic" in info undefined,
? { entry: info.dynamic.auxVerb } as T.VerbEntry info.type === "dynamic compound" ? "dynamic" : "generative stative"
: undefined; )
const blocks = [ : info.type === "dynamic or generative stative compound" &&
{ key: Math.random(), block: makeSubjectSelection(subject) }, os?.verb.voice !== "passive"
{ key: Math.random(), block: makeObjectSelection(object) }, ? makeNounSelection(
]; info.dynamic.objComplement.entry as T.NounEntry,
return { undefined,
blocks, "generative stative"
verb: { )
type: "verb", : transitivity === "transitive" && os?.verb.voice !== "passive"
verb: verb, ? getTransObjFromos()
dynAuxVerb, : "none";
verbTense: os ? os.verb.verbTense : "presentVerb", const isCompound =
perfectTense: os ? os.verb.perfectTense : "presentPerfect", "stative" in info && info.type === "dynamic or generative stative compound"
imperativeTense: os ? os.verb.imperativeTense : "imperfectiveImperative", ? "generative stative"
tenseCategory: os ? os.verb.tenseCategory : "basic", : "stative" in info || info.type === "stative compound"
transitivity, ? "stative"
isCompound, : info.type === "dynamic compound"
voice: transitivity === "transitive" ? "dynamic"
? (os?.verb.voice || "active") : false;
: "active", // TODO: here and below in the changeStatDyn function ... allow for entries with complement
negative: os ? os.verb.negative : false, const dynAuxVerb: T.VerbEntry | undefined =
canChangeTransitivity: "grammaticallyTransitive" in info, isCompound !== "dynamic"
canChangeVoice: transitivity === "transitive", ? undefined
canChangeStatDyn: "stative" in info, : info.type === "dynamic compound"
}, ? ({ entry: info.auxVerb } as T.VerbEntry)
externalComplement: takesExternalComplement(verb) : "dynamic" in info
? { type: "complement", selection: { type: "unselected" }} ? ({ entry: info.dynamic.auxVerb } as T.VerbEntry)
: undefined, : undefined;
form: (os && info.type !== "dynamic compound") const blocks = [
? os.form { key: Math.random(), block: makeSubjectSelection(subject) },
: { removeKing: false, shrinkServant: false }, { key: Math.random(), block: makeObjectSelection(object) },
}; ];
return {
blocks,
verb: {
type: "verb",
verb: verb,
dynAuxVerb,
verbTense: os ? os.verb.verbTense : "presentVerb",
perfectTense: os ? os.verb.perfectTense : "presentPerfect",
imperativeTense: os ? os.verb.imperativeTense : "imperfectiveImperative",
tenseCategory: os ? os.verb.tenseCategory : "basic",
transitivity,
isCompound,
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" } }
: undefined,
form:
os && info.type !== "dynamic compound"
? os.form
: { removeKing: false, shrinkServant: false },
};
} }
function takesExternalComplement(v: T.VerbEntry): boolean { function takesExternalComplement(v: T.VerbEntry): boolean {
if (v.entry.p === "کول" && v.entry.e.includes("to make")) { if (v.entry.p === "کول" && v.entry.e.includes("to make")) {
return true; return true;
} }
if (v.entry.p === "کېدل" && v.entry.e.includes("to become")) { if (v.entry.p === "کېدل" && v.entry.e.includes("to become")) {
return true; return true;
} }
return false; return false;
} }
export function changeStatDyn(v: T.VPSelectionState, s: "dynamic" | "stative"): T.VPSelectionState { export function changeStatDyn(
const info = getVerbInfo(v.verb.verb.entry, v.verb.verb.complement); v: T.VPSelectionState,
if (!("stative" in info)) { s: "dynamic" | "stative"
return v; ): T.VPSelectionState {
} const info = getVerbInfo(v.verb.verb.entry, v.verb.verb.complement);
return { if (!("stative" in info)) {
...v, return v;
blocks: adjustObjectSelection( }
v.blocks, const newBlocks = adjustObjectSelection(
s === "dynamic" v.blocks,
? { type: "NP", selection: makeNounSelection(info.dynamic.objComplement.entry as T.NounEntry, undefined, true) } s === "dynamic" ||
: undefined, (s === "stative" &&
), info.type === "dynamic or generative stative compound")
verb: { ? {
...v.verb, type: "NP",
isCompound: s, selection: makeNounSelection(
dynAuxVerb: s === "dynamic" info.dynamic.objComplement.entry as T.NounEntry,
? { entry: info.dynamic.auxVerb } as T.VerbEntry undefined,
: undefined, s === "dynamic" ? "dynamic" : "generative stative"
}, ),
}; }
: undefined
);
return {
...v,
blocks:
s === "stative" && info.type === "dynamic or generative stative compound"
? moveObjectToEnd(newBlocks)
: newBlocks,
verb: {
...v.verb,
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(
return { v: T.VPSelectionState,
...v, transitivity: "transitive" | "grammatically transitive"
blocks: adjustObjectSelection(v.blocks, transitivity === "grammatically transitive" ? T.Person.ThirdPlurMale : undefined), ): T.VPSelectionState {
verb: { return {
...v.verb, ...v,
transitivity, 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"); // throw new Error("unknown tense");
// } // }
export function getPersonFromNP(np: T.NPSelection): T.Person;
export function getPersonFromNP( 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; ): T.Person | undefined;
export function getPersonFromNP( export function getPersonFromNP(
np: T.NPSelection | T.ObjectNP np: T.NPSelection | T.Rendered<T.NPSelection> | T.ObjectNP
): T.Person | undefined { ): T.Person | undefined {
if (np === "none") { if (np === "none") {
return undefined; return undefined;

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -7,136 +7,138 @@
*/ */
module.exports = [ module.exports = [
1527816643, // استعفا کول - to resign, to quit (a job or position) 1527818320, // جارو کول - to sweep
1527817823, // اصرار کول - to insist, persist, demand 1658796089458, // استري کول - to iron
1591002320547, // امر کول - to order, command 1527816643, // استعفا کول - to resign, to quit (a job or position)
1527821339, // انتظار کول - to wait 1527817823, // اصرار کول - to insist, persist, demand
1527817226, // اندازه لګول - to guess, to estimate 1591002320547, // امر کول - to order, command
1527812167, // انکار کول - to deny, to renounce, to not recognize 1527821339, // انتظار کول - to wait
1527812598, // ایمان راوړل - to beileve, have faith (du chaa baande / د چا باندې) 1527817226, // اندازه لګول - to guess, to estimate
1527812002, // برخه اخستل - to take part in, to participate, to join in 1527812167, // انکار کول - to deny, to renounce, to not recognize
1527813489, // پرهېز کول - to abstain, fast 1527812598, // ایمان راوړل - to beileve, have faith (du chaa baande / د چا باندې)
1577390517316, // پرېکړه کول - to decide 1527812002, // برخه اخستل - to take part in, to participate, to join in
1527820710, // پناه اړول - to take/seek refuge 1527813489, // پرهېز کول - to abstain, fast
1527817312, // پوښتنه کول - to ask 1577390517316, // پرېکړه کول - to decide
1527818188, // پیروي کول - to follow, obey (usually a religious teacher or leader) 1527820710, // پناه اړول - to take/seek refuge
1527811863, // تاوان رسول - to harm, damage 1527817312, // پوښتنه کول - to ask
1527816383, // تپوس کول - to ask, to question, to request 1527818188, // پیروي کول - to follow, obey (usually a religious teacher or leader)
1527822770, // ترپکې وهل - to stamp (feet), to tread, to tap ones foot 1527811863, // تاوان رسول - to harm, damage
1527821587, // ترحم کول - to feel pity, to have sympathy, to feel sorry for someone 1527816383, // تپوس کول - to ask, to question, to request
1527818390, // تشریف راوړل - to come (when speaking about someone w/ respect) 1527822770, // ترپکې وهل - to stamp (feet), to tread, to tap ones foot
1527818391, // تشریف وړل - to go (when speaking about someone w/ respect) 1527821587, // ترحم کول - to feel pity, to have sympathy, to feel sorry for someone
1527814726, // تصمیم نیول - to make a decision 1527818390, // تشریف راوړل - to come (when speaking about someone w/ respect)
1579394718033, // تصور کول - to imagine, suppose 1527818391, // تشریف وړل - to go (when speaking about someone w/ respect)
1527815968, // تکیه کول - to rely on, to depend on 1527814726, // تصمیم نیول - to make a decision
1592303372377, // تلاوت کول - to read (relgious, usually the Quran) 1579394718033, // تصور کول - to imagine, suppose
1577551342853, // تمرکز کول - to concentrate, focus (په يوه شي باندې) 1527815968, // تکیه کول - to rely on, to depend on
1527815351, // توبه ایستل - to repent 1592303372377, // تلاوت کول - to read (relgious, usually the Quran)
1586452103064, // توجه کول - to pay attention to, to consider 1577551342853, // تمرکز کول - to concentrate, focus (په يوه شي باندې)
1527816822, // توکل کول - to count on, hope for, depend on 1527815351, // توبه ایستل - to repent
1527812186, // تیاري نیول - to prepare 1586452103064, // توجه کول - to pay attention to, to consider
1527814870, // ټوپونه وهل - to jump 1527816822, // توکل کول - to count on, hope for, depend on
1527815355, // ټوکې کول - to joke, tease, make fun of 1527812186, // تیاري نیول - to prepare
1527815867, // ټینګار کول - to emphasize, to insist 1527814870, // ټوپونه وهل - to jump
1527822741, // جیټکه خوړل - to be jolted, surprised 1527815355, // ټوکې کول - to joke, tease, make fun of
1527814864, // ځان وژل - to commit suicide 1527815867, // ټینګار کول - to emphasize, to insist
1527819607, // چرت وهل - to think about something, reflect on something, worry about something 1527822741, // جیټکه خوړل - to be jolted, surprised
1527821070, // چکر وهل - to stroll, walk around 1527814864, // ځان وژل - to commit suicide
1577812269585, // چمچه ګیري کول - to suck up to, to flatter 1527819607, // چرت وهل - to think about something, reflect on something, worry about something
1573768865232, // چنې وهل - to barter, bargain (for a price) 1527821070, // چکر وهل - to stroll, walk around
1527822814, // حسد کول - to be envious, to be jealous 1577812269585, // چمچه ګیري کول - to suck up to, to flatter
1527823161, // حفاظت کول - to protect, guard 1573768865232, // چنې وهل - to barter, bargain (for a price)
1527821042, // حمایه کول - to support, aid, protect, back 1527822814, // حسد کول - to be envious, to be jealous
1527818810, // حیا کول - to be bashful, modest, observing of Islamic rules of modesty 1527823161, // حفاظت کول - to protect, guard
1577823792516, // خدمت کول - to serve (د چا خدمت کول) 1527821042, // حمایه کول - to support, aid, protect, back
1588858155947, // خېز وهل - to jump, leap, bob up and down 1527818810, // حیا کول - to be bashful, modest, observing of Islamic rules of modesty
1589024311021, // دافع کول - to defend (د ځان دفاع کول - to defend oneself) 1577823792516, // خدمت کول - to serve (د چا خدمت کول)
1527816916, // دفاع کول - to defend, protect 1588858155947, // خېز وهل - to jump, leap, bob up and down
1527820291, // ډغره وهل - to challenge, invite to engage in a contest, confront, provoke 1589024311021, // دافع کول - to defend (د ځان دفاع کول - to defend oneself)
1527813125, // ذکر کول - to mention, refernce, remark, refer to 1527816916, // دفاع کول - to defend, protect
1527813937, // روژه نیول - to fast 1527820291, // ډغره وهل - to challenge, invite to engage in a contest, confront, provoke
1591804639647, // روغه کول - to reconcile, to make peace 1527813125, // ذکر کول - to mention, refernce, remark, refer to
1527813212, // زاري کول - to plea, ask, request, beg 1527813937, // روژه نیول - to fast
1584529741244, // زړه ساتل - to hold back and not say what you're really thinking 1591804639647, // روغه کول - to reconcile, to make peace
1575128717139, // زړه وهل - to feel hesitancy about some action or thing (د دې کار څخه زړه وهي); to satiate 1527813212, // زاري کول - to plea, ask, request, beg
1527813319, // زنګ وهل - to ring, phone, call (Afghanistan) 1584529741244, // زړه ساتل - to hold back and not say what you're really thinking
1527822368, // زیاتي کول - to do injustice, oppression (to someone) (د چا سرس زياتي کول) 1575128717139, // زړه وهل - to feel hesitancy about some action or thing (د دې کار څخه زړه وهي); to satiate
1527819178, // زیان رسول - to damage, to cause harm 1527813319, // زنګ وهل - to ring, phone, call (Afghanistan)
1594129207239, // سا اخستل - to breathe 1527822368, // زیاتي کول - to do injustice, oppression (to someone) (د چا سرس زياتي کول)
1594129204513, // سا اخیستل - to breathe 1527819178, // زیان رسول - to damage, to cause harm
1527815309, // سبق وایل - to study, go to school 1594129207239, // سا اخستل - to breathe
1527814102, // ست کول - to invite, to make an offer out of politeness (د ډوډۍ ست مې ورته وکړ) 1594129204513, // سا اخیستل - to breathe
1527818975, // سترګه وهل - to wink, to blink 1527815309, // سبق وایل - to study, go to school
1578326320888, // سجده لګول - to bowing down to the ground, to prostrating (in religion) 1527814102, // ست کول - to invite, to make an offer out of politeness (د ډوډۍ ست مې ورته وکړ)
1527816152, // سر ټکول - complain, gripe 1527818975, // سترګه وهل - to wink, to blink
1527816463, // سوله کول - to make peace, to reconcile 1578326320888, // سجده لګول - to bowing down to the ground, to prostrating (in religion)
1527818094, // سیل کول - to stroll through, go on a walk or tour through, to see, watch, examine, survey, visit, tour 1527816152, // سر ټکول - complain, gripe
1527814855, // شپېلۍ وهل - to play the flute, fife, reed, to whistle 1527816463, // سوله کول - to make peace, to reconcile
1527819033, // شکایت کول - to complain, to express a grievance 1527818094, // سیل کول - to stroll through, go on a walk or tour through, to see, watch, examine, survey, visit, tour
1577817988469, // شکست خوړل - to be defeated, to experience defeat 1527814855, // شپېلۍ وهل - to play the flute, fife, reed, to whistle
1527819185, // صبر کول - to be patient, to bear up and endure under difficult or painful circumstances 1527819033, // شکایت کول - to complain, to express a grievance
1527814887, // صفت کول - to praise; admire, glorify 1577817988469, // شکست خوړل - to be defeated, to experience defeat
1527818217, // طرفداري کول - to support, adhere to, pick a side, stand up for something or someone 1527819185, // صبر کول - to be patient, to bear up and endure under difficult or painful circumstances
1571946107980, // ظلم کول - to be cruel to, to oppress, to do voilence against, to persecute (د چا باندې ظلم کول) 1527814887, // صفت کول - to praise; admire, glorify
1581610643511, // عبادت کول - to worship, pray 1527818217, // طرفداري کول - to support, adhere to, pick a side, stand up for something or someone
1527811674, // عمل کول - to act, to put into practice; to do something addicting (like smoking), to do out of habit 1571946107980, // ظلم کول - to be cruel to, to oppress, to do voilence against, to persecute (د چا باندې ظلم کول)
1581610659810, // غچ اخیستل - to take revenge 1581610643511, // عبادت کول - to worship, pray
1527818401, // غرض کول - to interfere, to meddle, to step in 1527811674, // عمل کول - to act, to put into practice; to do something addicting (like smoking), to do out of habit
1592303194144, // غږ کول - to call out, to say something, make a sound 1581610659810, // غچ اخیستل - to take revenge
1578607689918, // غلا کول - to steal 1527818401, // غرض کول - to interfere, to meddle, to step in
1527818341, // غمرازي کول - to share in someones sorrow, to show sympathy or give condolences 1592303194144, // غږ کول - to call out, to say something, make a sound
1527818425, // غوټه اچول - to tie, fasten, hitch 1578607689918, // غلا کول - to steal
1527818422, // غوټه وهل - to dive, dip, go into water 1527818341, // غمرازي کول - to share in someones sorrow, to show sympathy or give condolences
1527812633, // غوږ نیول - to listen 1527818425, // غوټه اچول - to tie, fasten, hitch
1527816328, // غیبت کول - to gossip 1527818422, // غوټه وهل - to dive, dip, go into water
1588784260692, // فال اچول - to do fortune telling, divination 1527812633, // غوږ نیول - to listen
1527812607, // فکر کول - to think 1527816328, // غیبت کول - to gossip
1527822096, // قدم وهل - to take a step, to walk 1588784260692, // فال اچول - to do fortune telling, divination
1588152878869, // قرباني کول - to make a sacrifice 1527812607, // فکر کول - to think
1527817624, // قسم خوړل - to take an oath, vow, to swear 1527822096, // قدم وهل - to take a step, to walk
1527812732, // کار کول - to work 1588152878869, // قرباني کول - to make a sacrifice
1527811600, // کوشش کول - to try, to attempt, to put in effort 1527817624, // قسم خوړل - to take an oath, vow, to swear
1527819661, // ګپ لګول - to talk, converse; to joke 1527812732, // کار کول - to work
1527814357, // ګډون کول - to participate, join, be involved 1527811600, // کوشش کول - to try, to attempt, to put in effort
1582146016627, // ګذاره کول - to get by, make ends meet, deal with something, handle or get some task done (with difficulty or not quite in the ideal way), to bear with, to be tolerant or forgiving 1527819661, // ګپ لګول - to talk, converse; to joke
1527819872, // ګمان کول - to think, to suppose 1527814357, // ګډون کول - to participate, join, be involved
1579034883717, // لاړې تېرول - to spit ?? (other fluids too??) 1582146016627, // ګذاره کول - to get by, make ends meet, deal with something, handle or get some task done (with difficulty or not quite in the ideal way), to bear with, to be tolerant or forgiving
1527817357, // لاس وړل - to touch 1527819872, // ګمان کول - to think, to suppose
1527818937, // لامبو وهل - to swim, bathe 1579034883717, // لاړې تېرول - to spit ?? (other fluids too??)
1527813950, // لحاظ کول - to be considerate of, pay attention to someone or something, to be polite, to show deference or respect to someone 1527817357, // لاس وړل - to touch
1527813888, // لغته وهل - to kick 1527818937, // لامبو وهل - to swim, bathe
1527822099, // لمس کول - to touch, motivate, instigate 1527813950, // لحاظ کول - to be considerate of, pay attention to someone or something, to be polite, to show deference or respect to someone
1588760636420, // لوظ کول - to promise, give one's word 1527813888, // لغته وهل - to kick
1527819089, // ماته خوړل - to be defeated, beaten by someone 1527822099, // لمس کول - to touch, motivate, instigate
1527817361, // مخ اړول - to turn (ones face), to face, (when turning from someone or people) to neglect, (when turning to someone or people) to pay attention to 1588760636420, // لوظ کول - to promise, give one's word
1527812934, // مخه نیول - to prevent, hold back 1527819089, // ماته خوړل - to be defeated, beaten by someone
1588161314887, // مرسته کول - to help, assist 1527817361, // مخ اړول - to turn (ones face), to face, (when turning from someone or people) to neglect, (when turning to someone or people) to pay attention to
1527817165, // مزدوري کول - to do labour, to work 1527812934, // مخه نیول - to prevent, hold back
1609162269829, // مزې کول - to be enjoyable, to have good taste (the thing that gives enjoyment/taste "does maza"), to have fun 1588161314887, // مرسته کول - to help, assist
1579295606403, // ملاتړ کول - to support (ie. a political party etc.) 1527817165, // مزدوري کول - to do labour, to work
1589031340746, // موټر چلول - to drive a car 1609162269829, // مزې کول - to be enjoyable, to have good taste (the thing that gives enjoyment/taste "does maza"), to have fun
1527812902, // مینه کول - to love 1579295606403, // ملاتړ کول - to support (ie. a political party etc.)
1527817369, // ناره وهل - to cry out, to yell, to yell a chant or slogan 1589031340746, // موټر چلول - to drive a car
1527819687, // نارې کول - to cry out, shout (ناره) 1527812902, // مینه کول - to love
1527821254, // نافرماني کول - to disobey, to not comply, to rebel 1527817369, // ناره وهل - to cry out, to yell, to yell a chant or slogan
1527817709, // نجات موندل - to be saved, to find salvation 1527819687, // نارې کول - to cry out, shout (ناره)
1527823208, // نفرت کول - to hate, abhor 1527821254, // نافرماني کول - to disobey, to not comply, to rebel
1527811827, // نفس ایستل - breath in, inhale; kill 1527817709, // نجات موندل - to be saved, to find salvation
1579459605988, // نقصان کول - to suffer loss 1527823208, // نفرت کول - to hate, abhor
1527815991, // ننداره کول - to behold, to spectate, to watch, to take in 1527811827, // نفس ایستل - breath in, inhale; kill
1527823707, // نیالګی کېنول - to plant a sapling, young tree 1579459605988, // نقصان کول - to suffer loss
1527811729, // نیوکه کول - to criticize 1527815991, // ننداره کول - to behold, to spectate, to watch, to take in
1527823733, // هجرت کول - to migrate, resettle 1527823707, // نیالګی کېنول - to plant a sapling, young tree
1527820620, // هجوم کول - to swarm, rush, attack 1527811729, // نیوکه کول - to criticize
1527811599, // هڅه کول - to try, to attempt, to put in effort 1527823733, // هجرت کول - to migrate, resettle
1604431102462, // هدایت کول - to guide, show the true path, give revelation leading to truth 1527820620, // هجوم کول - to swarm, rush, attack
1527818092, // همکاري کول - to collaborate, to work together, to aid/help 1527811599, // هڅه کول - to try, to attempt, to put in effort
1527816106, // وده کول - to grow, develop, improve, rise 1604431102462, // هدایت کول - to guide, show the true path, give revelation leading to truth
1579723460957, // ورزش کول - to de exercise, athletics 1527818092, // همکاري کول - to collaborate, to work together, to aid/help
1527814910, // وعده کول - to promise 1527816106, // وده کول - to grow, develop, improve, rise
1527816263, // وفا کول - to be faithful, to keep one's promise 1579723460957, // ورزش کول - to de exercise, athletics
1609162463793, // واده کول - to marry, get married 1527814910, // وعده کول - to promise
1609599425410, // دعا کول - to pray 1527816263, // وفا کول - to be faithful, to keep one's promise
1527812939, // منډې وهل - to run 1609162463793, // واده کول - to marry, get married
1614602054303, // بدله اخیستل - to take revenge 1609599425410, // دعا کول - to pray
] 1527812939, // منډې وهل - to run
1614602054303, // بدله اخیستل - to take revenge
];

View File

@ -7,80 +7,81 @@
*/ */
module.exports = [ module.exports = [
1658537998960, // لېونی کول 1608137130992, // چیغه کول
1527812403, // بچ کول - to save, protect, guard, spare, rescue, economize 1658537998960, // لېونی کول
1577299232429, // بدلول - to change, to adapt, exchange, replace 1527812403, // بچ کول - to save, protect, guard, spare, rescue, economize
1527815728, // بلدول - to familiarize with, to acquaint, orient, to train 1577299232429, // بدلول - to change, to adapt, exchange, replace
1527821309, // بندول - to close, block, stop 1527815728, // بلدول - to familiarize with, to acquaint, orient, to train
1527821309, // بندول - to close, barricade, cut off, restrain, hold back 1527821309, // بندول - to close, block, stop
1527815843, // بېلول - to separate 1527821309, // بندول - to close, barricade, cut off, restrain, hold back
1588073727998, // پاکول - to clean, cleanse, purify 1527815843, // بېلول - to separate
1527812010, // پخلا کول - to reconcile, to bring to an agreement 1588073727998, // پاکول - to clean, cleanse, purify
1527820144, // پستول - to soften, knead, pulverize, dig over again 1527812010, // پخلا کول - to reconcile, to bring to an agreement
1584689306281, // پستول - to make soft, tender, gentle, loosened 1527820144, // پستول - to soften, knead, pulverize, dig over again
1583269391864, // پورته کول - to raise up, lift up, bring up 1584689306281, // پستول - to make soft, tender, gentle, loosened
1577394118297, // پوهول - to make understand (to bring someone to the state of understanding) 1583269391864, // پورته کول - to raise up, lift up, bring up
1571859113828, // پخول - to cook 1577394118297, // پوهول - to make understand (to bring someone to the state of understanding)
1527812385, // پیدا کول - to find, birth, create 1571859113828, // پخول - to cook
1581189437955, // پېش کول - to bring forward, present, deliver 1527812385, // پیدا کول - to find, birth, create
1527820021, // پېښول - to cause, provoke, bring on 1581189437955, // پېش کول - to bring forward, present, deliver
1591872434020, // پیلول - to start, begin 1527820021, // پېښول - to cause, provoke, bring on
1527815323, // تاوول - to turn, to twist (active, causative), to rotate (causative) to wrap up, wind up 1591872434020, // پیلول - to start, begin
1527812388, // تباه کول - to destroy, ruin 1527815323, // تاوول - to turn, to twist (active, causative), to rotate (causative) to wrap up, wind up
1580754885011, // ترکول - to abandon, leave, refuse 1527812388, // تباه کول - to destroy, ruin
1527821357, // تصدیق کول - to confirm, affirm, attest 1580754885011, // ترکول - to abandon, leave, refuse
1577501129214, // تکرار کول - to repeat 1527821357, // تصدیق کول - to confirm, affirm, attest
1527822697, // تولول - to weigh 1577501129214, // تکرار کول - to repeat
1579908304357, // تولول - to balance 1527822697, // تولول - to weigh
1579644522321, // تویول - to pour, shed, spill, scatter, knock down (fruit) 1579908304357, // تولول - to balance
1527815731, // ټولول - to gather, collect 1579644522321, // تویول - to pour, shed, spill, scatter, knock down (fruit)
1589019863017, // ټیک کول - to correct, make right 1527815731, // ټولول - to gather, collect
1527816201, // ثبتول - to enter, save, record, register 1589019863017, // ټیک کول - to correct, make right
1527821167, // جارول - to clean (with a brush), to warp (textiles); to sacrifice 1527816201, // ثبتول - to enter, save, record, register
1527816945, // جوتول - to make clear, evident, apparent, explained, established 1527821167, // جارول - to clean (with a brush), to warp (textiles); to sacrifice
1527816947, // جوتول - to harness, hitch up 1527816945, // جوتول - to make clear, evident, apparent, explained, established
1527812712, // جوړول - to make, form, build, mend, fix 1527816947, // جوتول - to harness, hitch up
1527817455, // ځایول - to place, put, accommodate, make room for, to make fit 1527812712, // جوړول - to make, form, build, mend, fix
1527815074, // چاپول - to print, publish 1527817455, // ځایول - to place, put, accommodate, make room for, to make fit
1527811693, // چاغول - to fatten up, to fatten, to make stout, plump 1527815074, // چاپول - to print, publish
1527816239, // خبرول - to inform, communicate, make known, notify 1527811693, // چاغول - to fatten up, to fatten, to make stout, plump
1527811395, // خپرول - to spread, disperse, open, unfold, publicize, distribute 1527816239, // خبرول - to inform, communicate, make known, notify
1527812222, // ختمول - to finish, complete, end, use up, destroy, kill 1527811395, // خپرول - to spread, disperse, open, unfold, publicize, distribute
1527814183, // خرڅول - to sell, to spend, (fig.) to betray 1527812222, // ختمول - to finish, complete, end, use up, destroy, kill
1577898915919, // خفه کول - to make sad, to grieve, to annoy; to choke, to make suffocate 1527814183, // خرڅول - to sell, to spend, (fig.) to betray
1592303701516, // خوږول - to sweeten, to make sweet, to delight, to give pleasure 1577898915919, // خفه کول - to make sad, to grieve, to annoy; to choke, to make suffocate
1527814174, // خوږول - to cause hurt, pain 1592303701516, // خوږول - to sweeten, to make sweet, to delight, to give pleasure
1527812811, // خوښول - to like, to choose, to select; to make happy 1527814174, // خوږول - to cause hurt, pain
1527813502, // درنول - to make heavier; to make serious, respectable, reliable 1527812811, // خوښول - to like, to choose, to select; to make happy
1527811432, // دفن کول - to bury 1527813502, // درنول - to make heavier; to make serious, respectable, reliable
1527813665, // ډکول - to fill 1527811432, // دفن کول - to bury
1527817258, // ډوبول - to cause to drown, to make sing, to submerge 1527813665, // ډکول - to fill
1527823503, // ډېرول - to increase, make more 1527817258, // ډوبول - to cause to drown, to make sing, to submerge
1527818347, // راپورته کول - to raise up 1527823503, // ډېرول - to increase, make more
1527823277, // رنګول - to paint, color 1527818347, // راپورته کول - to raise up
1527813179, // ړنګول - to destroy, wreck, ruin, demolish, mess up, liquidate, disband 1527823277, // رنګول - to paint, color
1591033078746, // ستړی کول - to make tired, wear out 1527813179, // ړنګول - to destroy, wreck, ruin, demolish, mess up, liquidate, disband
1527813065, // ستنول - to return, to give back; to delay someone or something, to direct, to turn, to fix on 1591033078746, // ستړی کول - to make tired, wear out
1527811949, // شریکول - to make a participant or involved, to share, to bring someone or something in, to include 1527813065, // ستنول - to return, to give back; to delay someone or something, to direct, to turn, to fix on
1589883890933, // شړمول - to attach loosely, to cause to be loose, lazy, flabby 1527811949, // شریکول - to make a participant or involved, to share, to bring someone or something in, to include
1527814493, // غرقول - to sink, plunge, submerge, immerse 1589883890933, // شړمول - to attach loosely, to cause to be loose, lazy, flabby
1527823133, // غلطول - confuse, mix up, make a mistake; to go wrong, to stray from; to deceive, to lead into error; to distract, to be distracted 1527814493, // غرقول - to sink, plunge, submerge, immerse
1527823366, // قبلول - to accept, to approve 1527823133, // غلطول - confuse, mix up, make a mistake; to go wrong, to stray from; to deceive, to lead into error; to distract, to be distracted
1527820386, // کلکول - to make firm, hard solid, to fasten, to secure, to lock, to staunchly defend 1527823366, // قبلول - to accept, to approve
1527814819, // ګرمول - to warm, to heat up, to heat 1527820386, // کلکول - to make firm, hard solid, to fasten, to secure, to lock, to staunchly defend
1579034597012, // ګیرول - to seize, catch, trap, confine, imprison, beseige, make stuck 1527814819, // ګرمول - to warm, to heat up, to heat
1588152253147, // لرې کول - to remove, put far away 1579034597012, // ګیرول - to seize, catch, trap, confine, imprison, beseige, make stuck
1527817121, // لندول - to make wet, moisten, to make damp, to soak 1588152253147, // لرې کول - to remove, put far away
1527817118, // لنډول - to shorten, to make short, abbreviate 1527817121, // لندول - to make wet, moisten, to make damp, to soak
1527814350, // لویول - to raise, bring up (children) 1527817118, // لنډول - to shorten, to make short, abbreviate
1527816012, // ماتول - to break, split, defeat 1527814350, // لویول - to raise, bring up (children)
1579387733916, // مینول - to cause to fall in love, to make to fall in love 1527816012, // ماتول - to break, split, defeat
1527817762, // هېرول - to forget 1579387733916, // مینول - to cause to fall in love, to make to fall in love
1589640176788, // ورکول - to lose/make lost, to misplace, to make dissapear, get rid off 1527817762, // هېرول - to forget
1527812004, // وقفول - to devote, dedicate, give, donate 1589640176788, // ورکول - to lose/make lost, to misplace, to make dissapear, get rid off
1579724723019, // ویدول - to put to sleep 1527812004, // وقفول - to devote, dedicate, give, donate
1579822065104, // ویښول - to wake up, to make awake, to make alert 1579724723019, // ویدول - to put to sleep
1527816559, // یادول - to remember, to recall, to think on, to call 1579822065104, // ویښول - to wake up, to make awake, to make alert
1527813556, // یو ځای کول - to gather, bring together 1527816559, // یادول - to remember, to recall, to think on, to call
1527815444, // زده کول - to learn, to teach 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: fp-ts@^2.16.0:
version "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== integrity sha512-bLq+KgbiXdTEoT1zcARrWEpa5z6A/8b7PcDW7Gef3NSisQ+VS7ll2Xbf1E+xsgik0rWub/8u0qP/iTTjj+PhxQ==
fragment-cache@^0.2.1: 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" resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
react-media-hook@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/react-media-hook/-/react-media-hook-0.5.0.tgz#f830231f31ea80049f8cbaf8058da90ab71e7150"
integrity sha512-OupDgOSCjUUWPiXq3HMoRwpsQry4cGf4vKzh2E984Xtm4I01ZFbq8JwCG/RPqXB9h0qxgzoYLbABC+LIZH8deQ==
react-overlays@^5.1.1: react-overlays@^5.1.1:
version "5.1.1" version "5.1.1"
resolved "https://registry.npmjs.org/react-overlays/-/react-overlays-5.1.1.tgz" resolved "https://registry.npmjs.org/react-overlays/-/react-overlays-5.1.1.tgz"