BIG IMPROVEMENT - passive and ability stems, simplified formulas, and avoiding picking passive ability verbs etc

This commit is contained in:
lingdocs 2022-07-27 16:45:52 -05:00
parent 8f559e0665
commit 0008f18e85
9 changed files with 179 additions and 27 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@lingdocs/pashto-inflector", "name": "@lingdocs/pashto-inflector",
"version": "3.5.9", "version": "3.6.0",
"author": "lingdocs.com", "author": "lingdocs.com",
"description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations", "description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations",
"homepage": "https://verbs.lingdocs.com", "homepage": "https://verbs.lingdocs.com",

View File

@ -41,12 +41,13 @@ const title: CSSProperties = {
export function RootsAndStems({ textOptions, info, hidePastParticiple, highlighted, noTails }: { export function RootsAndStems({ textOptions, info, hidePastParticiple, highlighted, noTails }: {
textOptions: T.TextOptions, textOptions: T.TextOptions,
info: T.NonComboVerbInfo | T.PassiveRootsStems, info: T.NonComboVerbInfo | T.PassiveRootsAndStems | T.AbilityRootsAndStems,
hidePastParticiple?: boolean, hidePastParticiple?: boolean,
highlighted?: T.RootsOrStemsToHighlight, highlighted?: T.RootsOrStemsToHighlight,
noTails?: boolean, noTails?: boolean,
}) { }) {
const hasPerfectiveSplit = !!(info.root.perfectiveSplit || info.stem.perfectiveSplit); const hasPerfectiveSplit = ("perfectiveSplit" in info.root && "perfectiveSplit" in info.stem)
&& !!(info.root.perfectiveSplit || info.stem.perfectiveSplit);
const showPersInf = hasPersInfs(info); const showPersInf = hasPersInfs(info);
const [persInf, setPersInf] = useState<T.PersonInflectionsField>("mascSing"); const [persInf, setPersInf] = useState<T.PersonInflectionsField>("mascSing");
const [split, setSplit] = useState<boolean>(false); const [split, setSplit] = useState<boolean>(false);
@ -161,7 +162,7 @@ export function RootsAndStems({ textOptions, info, hidePastParticiple, highlight
</div> </div>
</div> </div>
</div> </div>
{!hidePastParticiple && <div className="text-center" style={highlighted?.includes("past participle") ? highlight : {}}> {!hidePastParticiple && "participle" in info &&<div className="text-center" style={highlighted?.includes("past participle") ? highlight : {}}>
<div style={title}>Past Participle</div> <div style={title}>Past Participle</div>
<VerbInfoItemDisplay <VerbInfoItemDisplay
item={pickPersInf(info.participle.past, persInf)} item={pickPersInf(info.participle.past, persInf)}

View File

@ -8,48 +8,50 @@ import {
VpsReducerAction VpsReducerAction
} from "./vps-reducer"; } from "./vps-reducer";
const verbTenseOptions: { label: string | JSX.Element, value: T.VerbTense, formula: string, modalFormula: string, }[] = [{ const verbTenseOptions: { label: string | JSX.Element, value: T.VerbTense, formula: string }[] = [{
label: <div><i className="fas fa-video mr-2" />present</div>, label: <div><i className="fas fa-video mr-2" />present</div>,
value: "presentVerb", value: "presentVerb",
formula: "imperfective stem + present verb ending", formula: "imperfective stem + present verb ending",
modalFormula: `imperfective root + tail + kedul "to become" subjunctive`,
}, { }, {
label: <div><i className="fas fa-camera mr-2" />subjunctive</div>, label: <div><i className="fas fa-camera mr-2" />subjunctive</div>,
value: "subjunctiveVerb", value: "subjunctiveVerb",
formula: "perfective stem + present verb ending", formula: "perfective stem + present verb ending",
modalFormula: `perfective root + tail + kedul "to become" subjunctive`,
}, { }, {
label: <div><i className="fas fa-video mr-2" />imperfective future</div>, label: <div><i className="fas fa-video mr-2" />imperfective future</div>,
value: "imperfectiveFuture", value: "imperfectiveFuture",
formula: "ba + present", formula: "ba + present",
modalFormula: `ba + present modal`,
}, { }, {
label: <div><i className="fas fa-camera mr-2" />perfective future</div>, label: <div><i className="fas fa-camera mr-2" />perfective future</div>,
value: "perfectiveFuture", value: "perfectiveFuture",
formula: "ba + subjunctive", formula: "ba + subjunctive",
modalFormula: `ba + subjunctive modal`,
}, { }, {
label: <div><i className="fas fa-video mr-2" />continuous past</div>, label: <div><i className="fas fa-video mr-2" />continuous past</div>,
value: "imperfectivePast", value: "imperfectivePast",
formula: "imperfective root + past verb ending", formula: "imperfective root + past verb ending",
modalFormula: `imperfective root + tail + kedul "to become" simple past`,
}, { }, {
label: <div><i className="fas fa-camera mr-2" />simple past</div>, label: <div><i className="fas fa-camera mr-2" />simple past</div>,
value: "perfectivePast", value: "perfectivePast",
formula: "perfective root + past verb ending", formula: "perfective root + past verb ending",
modalFormula: `perfective root + tail + kedul "to become" simple past`,
}, { }, {
label: <div><i className="fas fa-video mr-2" />habitual continual past</div>, label: <div><i className="fas fa-video mr-2" />habitual continual past</div>,
value: "habitualImperfectivePast", value: "habitualImperfectivePast",
formula: "ba + contiunous past", formula: "ba + continuous past",
modalFormula: `ba + simple past modal`,
}, { }, {
label: <div><i className="fas fa-camera mr-2" />habitual simple past</div>, label: <div><i className="fas fa-camera mr-2" />habitual simple past</div>,
value: "habitualPerfectivePast", value: "habitualPerfectivePast",
formula: "ba + simple past", formula: "ba + simple past",
modalFormula: `ba + continuous past modal`,
}]; }];
function composeFormula(formula: string, prefix: "passive" | "ability"): string {
return formula.replace(/^perfective/, `${prefix} perfective`)
.replace(/^imperfective/, `${prefix} imperfective`)
.replace("continuous", `${prefix} continuous`)
.replace("simple", `${prefix} simple`)
.replace(/present$/, `${prefix} present`)
.replace(/subjunctive$/, `${prefix} subjunctive`)
.replace("past participle", `${prefix} past participle`);
}
const perfectTenseOptions: { label: string | JSX.Element, value: T.PerfectTense, formula: string }[] = [{ const perfectTenseOptions: { label: string | JSX.Element, value: T.PerfectTense, formula: string }[] = [{
label: "Present Perfect", label: "Present Perfect",
value: "presentPerfect", value: "presentPerfect",
@ -181,6 +183,7 @@ function TensePicker(props: ({
: verbTenseOptions; : verbTenseOptions;
const showImperativeOption = ("vps" in props && props.vps.verb.voice === "active") const showImperativeOption = ("vps" in props && props.vps.verb.voice === "active")
|| ("vpsComplete" in props && props.vpsComplete.verb.voice !== "active"); || ("vpsComplete" in props && props.vpsComplete.verb.voice !== "active");
const inPassiveVoice = ("vps" in props && props.vps.verb.voice === "passive") || ("vpsComplete" in props && props.vpsComplete.verb.voice === "passive");;
const canHaveFormula = "vps" in props && props.mode !== "quiz"; const canHaveFormula = "vps" in props && props.mode !== "quiz";
return <div> return <div>
<div style={{ maxWidth: "300px", minWidth: "250px", margin: "0 auto" }}> <div style={{ maxWidth: "300px", minWidth: "250px", margin: "0 auto" }}>
@ -196,6 +199,7 @@ function TensePicker(props: ({
value={"vpsComplete" in props value={"vpsComplete" in props
? getTenseCategory(props.vpsComplete.verb.tense) ? getTenseCategory(props.vpsComplete.verb.tense)
: props.vps.verb.tenseCategory} : props.vps.verb.tenseCategory}
// @ts-ignore
options={showImperativeOption ? [{ options={showImperativeOption ? [{
label: "Basic", label: "Basic",
value: "basic", value: "basic",
@ -217,7 +221,7 @@ function TensePicker(props: ({
}, { }, {
label: "Ability", label: "Ability",
value: "modal", value: "modal",
}]} }].filter(x => !(inPassiveVoice && x.value === "modal"))}
handleChange={props.mode !== "quiz" ? onTenseCategorySelect : () => null} handleChange={props.mode !== "quiz" ? onTenseCategorySelect : () => null}
/> />
</div>} </div>}
@ -276,8 +280,10 @@ function TensePicker(props: ({
]); ]);
const formula = !curr const formula = !curr
? "" ? ""
: ("modalFormula" in curr && props.vps.verb.tenseCategory === "modal") : (props.vps.verb.tenseCategory === "modal")
? curr.modalFormula ? composeFormula(curr.formula, "ability")
: (props.vps.verb.voice === "passive")
? composeFormula(curr.formula, "passive")
: curr.formula; : curr.formula;
if (curr && "formula" in curr) { if (curr && "formula" in curr) {
return <div className="mb-2" style={{ width: "250px", overflowY: "auto" }}> return <div className="mb-2" style={{ width: "250px", overflowY: "auto" }}>

View File

@ -1,7 +1,7 @@
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 { getPassiveRootsAndStems, getVerbInfo } from "../../lib/verb-info"; import { getAbilityRootsAndStems, getPassiveRootsAndStems, getVerbInfo } from "../../lib/verb-info";
import Hider from "../Hider"; import Hider from "../Hider";
import useStickyState from "../../lib/useStickyState"; import useStickyState from "../../lib/useStickyState";
import CompoundDisplay from "./CompoundDisplay"; import CompoundDisplay from "./CompoundDisplay";
@ -51,6 +51,14 @@ function VerbPicker(props: {
}); });
} }
const passiveRootsAndStems = (info && props.vps.verb.voice === "passive") ? getPassiveRootsAndStems(info) : undefined; const passiveRootsAndStems = (info && props.vps.verb.voice === "passive") ? getPassiveRootsAndStems(info) : undefined;
const abilityRootsAndStems = (() => {
try {
return (info && props.vps.verb.tenseCategory === "modal") ? getAbilityRootsAndStems(info) : undefined;
} catch (e) {
console.log("error making ability roots and stems", e);
return undefined;
}
})();
return <div className="mb-3"> return <div className="mb-3">
{info && <CompoundDisplay {info && <CompoundDisplay
info={info} info={info}
@ -60,13 +68,17 @@ function VerbPicker(props: {
{info && <div className="mt-3 mb-1 text-center"> {info && <div className="mt-3 mb-1 text-center">
<Hider <Hider
showing={showRootsAndStems} showing={showRootsAndStems}
label="🌳 Roots and Stems" label={`🌳 ${passiveRootsAndStems ? "Passive" : abilityRootsAndStems ? "Ability" : ""} Roots and Stems`}
handleChange={() => setShowRootsAndStems(p => !p)} handleChange={() => setShowRootsAndStems(p => !p)}
hLevel={5} hLevel={5}
> >
<RootsAndStems <RootsAndStems
textOptions={props.opts} textOptions={props.opts}
info={passiveRootsAndStems ? passiveRootsAndStems : info} info={passiveRootsAndStems
? passiveRootsAndStems
: abilityRootsAndStems
? abilityRootsAndStems
: info}
/> />
</Hider> </Hider>
</div>} </div>}
@ -89,7 +101,7 @@ function VerbPicker(props: {
<ButtonSelect <ButtonSelect
small small
value={props.vps.verb.voice} value={props.vps.verb.voice}
options={props.vps.verb.tenseCategory === "imperative" options={(props.vps.verb.tenseCategory === "imperative" || props.vps.verb.tenseCategory === "modal")
? [{ ? [{
label: "Active", label: "Active",
value: "active", value: "active",

View File

@ -155,6 +155,7 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
verb: { verb: {
...vps.verb, ...vps.verb,
voice, voice,
tenseCategory: vps.verb.tenseCategory === "modal" ? "basic" : vps.verb.tenseCategory,
}, },
}; };
} else { } else {
@ -239,6 +240,16 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
}, },
}); });
} }
if (category === "modal") {
return {
...vps,
verb: {
...vps.verb,
tenseCategory: category,
voice: "active",
},
}
}
return { return {
...vps, ...vps,
verb: { verb: {

View File

@ -60,12 +60,18 @@ export function getFirstSecThird(p: T.Person): 1 | 2 | 3 {
// return s; // return s;
// } // }
export function hasPersInfs(info: T.NonComboVerbInfo | T.PassiveRootsStems): boolean { export function hasPersInfs(info: T.NonComboVerbInfo | T.PassiveRootsAndStems | T.AbilityRootsAndStems): boolean {
if ("participle" in info) {
return (
"mascSing" in info.root.perfective ||
"mascSing" in info.stem.perfective ||
("present" in info.participle && "mascSing" in info.participle.present) ||
"mascSing" in info.participle.past
);
}
return ( return (
"mascSing" in info.root.perfective || "mascSing" in info.root.perfective ||
"mascSing" in info.stem.perfective || "mascSing" in info.stem.perfective
("present" in info.participle && "mascSing" in info.participle.present) ||
"mascSing" in info.participle.past
); );
} }

View File

@ -21,6 +21,7 @@ import {
choosePersInf, choosePersInf,
isUnisexSet, isUnisexSet,
getLong, getLong,
getShort,
} from "./p-text-helpers"; } from "./p-text-helpers";
import { import {
makePsString, makePsString,
@ -39,6 +40,7 @@ import {
} from "./pashto-inflector"; } from "./pashto-inflector";
import { import {
checkForIrregularConjugation, checkForIrregularConjugation,
kedulStat,
stativeAux, stativeAux,
} from "./irregular-conjugations"; } from "./irregular-conjugations";
import { import {
@ -53,6 +55,7 @@ import {
spaceInForm, spaceInForm,
getAuxTransitivity, getAuxTransitivity,
chooseParticipleInflection, chooseParticipleInflection,
noPersInfs,
} from "./misc-helpers"; } from "./misc-helpers";
import * as T from "../types"; import * as T from "../types";
@ -982,7 +985,72 @@ function makeDynamicPerfectiveSplit(comp: T.PsString, auxSplit: T.SplitInfo): T.
]; ];
} }
export function getPassiveRootsAndStems(info: T.NonComboVerbInfo, withTails?: boolean): T.PassiveRootsStems | undefined { export function getAbilityRootsAndStems(info: T.NonComboVerbInfo): T.AbilityRootsAndStems {
const isIntransitiveStativeCompound = info.type === "stative compound" && info.transitivity === "intransitive"
const roots = getAbilityRoots(info.root, isIntransitiveStativeCompound);
return addAbilityHelperRootsAndStems(roots, isIntransitiveStativeCompound);
}
function addAbilityHelperRootsAndStems(roots: T.VerbRootSet, isIntransitiveStativeCompound: boolean): T.AbilityRootsAndStems {
function addAbilityHelperToRoot(
r: T.OptionalPersonInflections<T.LengthOptions<T.PsString>>,
helper: T.PsString,
): T.OptionalPersonInflections<T.LengthOptions<T.PsString>> {
if ("mascSing" in r) {
return {
mascSing: addAbilityHelperToRoot(r.mascSing, helper) as T.LengthOptions<T.PsString>,
mascPlur: addAbilityHelperToRoot(r.mascPlur, helper) as T.LengthOptions<T.PsString>,
femSing: addAbilityHelperToRoot(r.femSing, helper) as T.LengthOptions<T.PsString>,
femPlur: addAbilityHelperToRoot(r.femPlur, helper) as T.LengthOptions<T.PsString>,
}
}
return {
long: concatPsString(r.long, " ", helper),
short: concatPsString(r.short, " ", helper),
};
}
const stemHelper = getLong(noPersInfs(kedulStat.info.stem.perfective));
const rootHelper = noPersInfs(kedulStat.info.root.perfective).long;
return {
stem: {
perfective: addAbilityHelperToRoot(roots.perfective, stemHelper),
imperfective: addAbilityHelperToRoot(roots.imperfective, stemHelper),
...roots.perfectiveSplit ? {
perfectiveSplit: addAbilityHelperToPerfectiveSplit(roots.perfectiveSplit, stemHelper),
} : {},
},
root: {
perfective: addAbilityHelperToRoot(roots.perfective, rootHelper),
imperfective: addAbilityHelperToRoot(roots.imperfective, rootHelper),
...roots.perfectiveSplit ? {
perfectiveSplit: addAbilityHelperToPerfectiveSplit(roots.perfectiveSplit, rootHelper),
} : {},
},
};
}
function addAbilityHelperToPerfectiveSplit(s: T.SplitInfo, helper: T.PsString): T.SplitInfo {
if ("mascSing" in s) {
return {
mascSing: addAbilityHelperToPerfectiveSplit(s.mascSing, helper) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>,
mascPlur: addAbilityHelperToPerfectiveSplit(s.mascPlur, helper) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>,
femSing: addAbilityHelperToPerfectiveSplit(s.femSing, helper) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>,
femPlur: addAbilityHelperToPerfectiveSplit(s.femPlur, helper) as T.SingleOrLengthOpts<[T.PsString, T.PsString]>,
};
}
return {
long: [
getLong(s)[0],
concatPsString(getLong(s)[1], " ", helper),
],
short: [
getShort(s)[0],
concatPsString(getShort(s)[1], " ", helper),
],
};
}
export function getPassiveRootsAndStems(info: T.NonComboVerbInfo, withTails?: boolean): T.PassiveRootsAndStems | undefined {
if (info.transitivity === "intransitive") return undefined; if (info.transitivity === "intransitive") return undefined;
return { return {
stem: getPassiveStem(info.root, info.root.perfectiveSplit, withTails), stem: getPassiveStem(info.root, info.root.perfectiveSplit, withTails),
@ -1050,6 +1118,50 @@ function getPassiveRootPerfectiveSplit(root: T.OptionalPersonInflections<T.Lengt
}; };
} }
const abilityTail = { p: "ی", f: "ey" };
const abilityTailAccented = { p: "ی", f: "éy" };
function getAbilityRoots(root: T.VerbRootSet, isIntransitiveStativeCompound: boolean): T.VerbRootSet {
function getAspectAbilityRoot(root: T.VerbRootSet[T.Aspect], aspect: T.Aspect): T.OptionalPersonInflections<T.LengthOptions<T.PsString>> {
if ("mascSing" in root) {
return {
mascSing: getAspectAbilityRoot(root.mascSing, aspect) as T.LengthOptions<T.PsString>,
mascPlur: getAspectAbilityRoot(root.mascPlur, aspect) as T.LengthOptions<T.PsString>,
femSing: getAspectAbilityRoot(root.femSing, aspect) as T.LengthOptions<T.PsString>,
femPlur: getAspectAbilityRoot(root.femPlur, aspect) as T.LengthOptions<T.PsString>,
};
}
return {
long: concatPsString(root.long, abilityTail) as T.PsString,
short: concatPsString(root.short, aspect === "imperfective" ? abilityTailAccented : abilityTail) as T.PsString,
}
}
function getAbilityRootPerfectiveSplit(s: T.SplitInfo): T.SplitInfo {
if ("mascSing" in s) {
return {
mascSing: getAbilityRootPerfectiveSplit(s.mascSing) as [T.PsString, T.PsString],
mascPlur: getAbilityRootPerfectiveSplit(s.mascPlur) as [T.PsString, T.PsString],
femSing: getAbilityRootPerfectiveSplit(s.femSing) as [T.PsString, T.PsString],
femPlur: getAbilityRootPerfectiveSplit(s.femPlur) as [T.PsString, T.PsString],
};
}
return {
long: [getLong(s)[0], concatPsString(getLong(s)[1], abilityTail)],
short: [getShort(s)[0], concatPsString(getShort(s)[1], abilityTail)],
}
}
return {
perfective: getAspectAbilityRoot(
!isIntransitiveStativeCompound ? root.perfective : root.imperfective,
!isIntransitiveStativeCompound ? "perfective" : "imperfective",
),
imperfective: getAspectAbilityRoot(root.imperfective, "imperfective"),
...(root.perfectiveSplit && !isIntransitiveStativeCompound) ? {
perfectiveSplit: getAbilityRootPerfectiveSplit(root.perfectiveSplit),
} : {},
};
}
function getPassiveRoot(root: T.VerbRootSet, splitInfo: T.SplitInfo | undefined, withTails?: boolean): T.VerbRootSet { function getPassiveRoot(root: T.VerbRootSet, splitInfo: T.SplitInfo | undefined, withTails?: boolean): T.VerbRootSet {
const perfectiveRoot = withTails ? concatPsString(root.perfective, passiveRootTail) : root.perfective; const perfectiveRoot = withTails ? concatPsString(root.perfective, passiveRootTail) : root.perfective;
const imperfectiveRoot = withTails ? concatPsString(root.imperfective, passiveRootTail) : root.imperfective; const imperfectiveRoot = withTails ? concatPsString(root.imperfective, passiveRootTail) : root.imperfective;

View File

@ -15,6 +15,7 @@ import {
import { import {
getVerbInfo, getVerbInfo,
getPassiveRootsAndStems, getPassiveRootsAndStems,
getAbilityRootsAndStems,
} from "./lib/verb-info"; } from "./lib/verb-info";
import InflectionsTable from "./components/InflectionsTable"; import InflectionsTable from "./components/InflectionsTable";
import Pashto from "./components/Pashto"; import Pashto from "./components/Pashto";
@ -173,6 +174,7 @@ export {
conjugateVerb, conjugateVerb,
getVerbInfo, getVerbInfo,
getPassiveRootsAndStems, getPassiveRootsAndStems,
getAbilityRootsAndStems,
inflectWord, inflectWord,
addToForm, addToForm,
concatPsString, concatPsString,

View File

@ -169,7 +169,7 @@ export type VerbInfoBase = {
idiosyncraticThirdMascSing?: ShortThirdPersFormSet; idiosyncraticThirdMascSing?: ShortThirdPersFormSet;
} }
export type PassiveRootsStems = { export type PassiveRootsAndStems = {
stem: VerbStemSet, stem: VerbStemSet,
root: VerbRootSet, root: VerbRootSet,
participle: { participle: {
@ -177,6 +177,8 @@ export type PassiveRootsStems = {
}, },
} }
export type AbilityRootsAndStems = Omit<PassiveRootsAndStems, "participle">;
export type SimpleVerbInfo = VerbInfoBase & { export type SimpleVerbInfo = VerbInfoBase & {
type: "simple"; type: "simple";
} }