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",
"version": "3.5.9",
"version": "3.6.0",
"author": "lingdocs.com",
"description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations",
"homepage": "https://verbs.lingdocs.com",

View File

@ -41,12 +41,13 @@ const title: CSSProperties = {
export function RootsAndStems({ textOptions, info, hidePastParticiple, highlighted, noTails }: {
textOptions: T.TextOptions,
info: T.NonComboVerbInfo | T.PassiveRootsStems,
info: T.NonComboVerbInfo | T.PassiveRootsAndStems | T.AbilityRootsAndStems,
hidePastParticiple?: boolean,
highlighted?: T.RootsOrStemsToHighlight,
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 [persInf, setPersInf] = useState<T.PersonInflectionsField>("mascSing");
const [split, setSplit] = useState<boolean>(false);
@ -161,7 +162,7 @@ export function RootsAndStems({ textOptions, info, hidePastParticiple, highlight
</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>
<VerbInfoItemDisplay
item={pickPersInf(info.participle.past, persInf)}

View File

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

View File

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

View File

@ -155,6 +155,7 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
verb: {
...vps.verb,
voice,
tenseCategory: vps.verb.tenseCategory === "modal" ? "basic" : vps.verb.tenseCategory,
},
};
} 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 {
...vps,
verb: {

View File

@ -60,12 +60,18 @@ export function getFirstSecThird(p: T.Person): 1 | 2 | 3 {
// 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 (
"mascSing" in info.root.perfective ||
"mascSing" in info.stem.perfective ||
("present" in info.participle && "mascSing" in info.participle.present) ||
"mascSing" in info.participle.past
"mascSing" in info.stem.perfective
);
}

View File

@ -21,6 +21,7 @@ import {
choosePersInf,
isUnisexSet,
getLong,
getShort,
} from "./p-text-helpers";
import {
makePsString,
@ -39,6 +40,7 @@ import {
} from "./pashto-inflector";
import {
checkForIrregularConjugation,
kedulStat,
stativeAux,
} from "./irregular-conjugations";
import {
@ -53,6 +55,7 @@ import {
spaceInForm,
getAuxTransitivity,
chooseParticipleInflection,
noPersInfs,
} from "./misc-helpers";
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;
return {
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 {
const perfectiveRoot = withTails ? concatPsString(root.perfective, passiveRootTail) : root.perfective;
const imperfectiveRoot = withTails ? concatPsString(root.imperfective, passiveRootTail) : root.imperfective;

View File

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

View File

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