include imperative

This commit is contained in:
lingdocs 2022-04-20 19:05:15 +05:00
parent 875237439b
commit 10532cb3bb
19 changed files with 310 additions and 102 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@lingdocs/pashto-inflector",
"version": "2.1.2",
"version": "2.1.3",
"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

@ -69,7 +69,6 @@ function App() {
}, [theme])
const handleVerbIndexChange = (e: any) => {
console.log("changing to", e.target.value);
setVerbTs(parseInt(e.target.value));
}
const handleTypeSelection = (e: any) => {

View File

@ -14,7 +14,6 @@ type PsStringWSub = T.PsString & { sub?: any };
function EnglishContent({ children }: { children: (string | JSX.Element)[] | (string | JSX.Element) }) {
if (Array.isArray(children)) {
console.log(children);
return <>
{children.map((x) => <EnglishContent>{x}</EnglishContent>)}
</>

View File

@ -40,7 +40,7 @@ function agreementInfo(info: T.NonComboVerbInfo, displayForm: T.DisplayForm): Re
}
function VerbFormDisplay({ displayForm, textOptions, info, showingFormInfo, english, shortDefault }: {
displayForm: T.DisplayForm | T.VerbForm,
displayForm: T.DisplayForm | T.VerbForm | T.ImperativeForm,
english?: T.EnglishBlock | string,
textOptions: T.TextOptions,
showingFormInfo: boolean,

View File

@ -8,6 +8,7 @@ import {
} from "../../lib/np-tools";
import { useState, useEffect } from "react";
import * as T from "../../types";
import { isSecondPerson } from "../../lib/phrase-building/vp-tools";
// import { capitalizeFirstLetter } from "../../lib/text-tools";
const npTypes: T.NPType[] = ["pronoun", "noun", "participle"];
@ -19,6 +20,7 @@ function NPPicker(props: {
asObject?: boolean,
opts: T.TextOptions,
cantClear?: boolean,
is2ndPersonPicker?: boolean,
} & ({
nouns: (s: string) => T.NounEntry[],
verbs: (s: string) => T.VerbEntry[],
@ -28,6 +30,9 @@ function NPPicker(props: {
nouns: T.NounEntry[],
verbs: T.VerbEntry[],
})) {
if (props.is2ndPersonPicker && ((props.np?.type !== "pronoun") || !isSecondPerson(props.np.person))) {
throw new Error("can't use 2ndPerson NPPicker without a pronoun");
}
const [npType, setNpType] = useState<T.NPType | undefined>(props.np ? props.np.type : undefined);
useEffect(() => {
setNpType(props.np ? props.np.type : undefined);
@ -78,6 +83,7 @@ function NPPicker(props: {
pronoun={props.np}
onChange={props.onChange}
clearButton={clearButton}
is2ndPersonPicker={props.is2ndPersonPicker}
opts={props.opts}
/>
: npType === "noun"

View File

@ -2,6 +2,9 @@ import * as T from "../../types";
import ButtonSelect from "../ButtonSelect";
import useStickyState from "../../lib/useStickyState";
import classNames from "classnames";
import {
isSecondPerson, isThirdPerson,
} from "../../lib/phrase-building/vp-tools";
const gColors = {
masc: "LightSkyBlue",
@ -53,15 +56,18 @@ function pickerStateToPerson(s: PickerState): T.Person {
+ (6 * s.col);
}
function NPPronounPicker({ onChange, pronoun, asObject, clearButton, opts }: {
function NPPronounPicker({ onChange, pronoun, asObject, clearButton, opts, is2ndPersonPicker }: {
pronoun: T.PronounSelection,
onChange: (p: T.PronounSelection) => void,
asObject?: boolean,
clearButton?: JSX.Element,
opts: T.TextOptions,
is2ndPersonPicker?: boolean,
}) {
if (is2ndPersonPicker && !isSecondPerson(pronoun.person)) {
throw new Error("can't use 2ndPerson NPProunounPicker without a pronoun");
}
const [display, setDisplay] = useStickyState<"persons" | "p" | "e">("e", "prounoun-picker-display");
const p = personToPickerState(pronoun.person);
function handleClick(row: number, col: number) {
const person = pickerStateToPerson({ ...p, row, col });
@ -92,11 +98,14 @@ function NPPronounPicker({ onChange, pronoun, asObject, clearButton, opts }: {
setDisplay(newPerson);
}
const prs = labels(!!asObject)[display];
const pSpec = "near" in prs ? prs[pronoun.distance] : prs;
const pSpecA = "near" in prs ? prs[pronoun.distance] : prs;
const pSpec = is2ndPersonPicker
? [pSpecA[1]]
: pSpecA;
return <div style={{ maxWidth: "145px", padding: 0 }}>
{clearButton}
<div className="d-flex flex-row justify-content-around mb-3">
<ButtonSelect
<div className="d-flex flex-row justify-content-between mb-3">
{isThirdPerson(pronoun.person) ? <ButtonSelect
xSmall
options={[
{ label: "Far", value: "far" },
@ -104,7 +113,7 @@ function NPPronounPicker({ onChange, pronoun, asObject, clearButton, opts }: {
]}
value={pronoun.distance}
handleChange={(g) => handlePronounTypeChange(g as "far" | "near")}
/>
/> : <div>{` `}</div>}
<button className="btn btn-sm btn-outline-secondary" onClick={handleDisplayChange}>
{display === "persons" ? "#" : display === "p" ? "PS" : "EN"}
</button>
@ -114,9 +123,13 @@ function NPPronounPicker({ onChange, pronoun, asObject, clearButton, opts }: {
{pSpec.map((rw, i) => (
<tr>
{rw.map((r, j) => {
const active = (p.row === i && p.col === j)
const active = is2ndPersonPicker
? (p.col === j)
: (p.row === i && p.col === j);
return <td
onClick={() => handleClick(i, j)}
onClick={() => {
handleClick(is2ndPersonPicker ? 1 : i, j);
}}
className={classNames({ "table-active": active, "text-on-gender-color": active })}
style={{
backgroundColor: active ? gColors[p.gender] : "inherit",

View File

@ -16,7 +16,7 @@ function ChartDisplay({ VS, opts }: { VS: T.VerbSelection, opts: T.TextOptions }
: ("transitive" in rawConjugations)
? rawConjugations[VS.transitivity === "grammatically transitive" ? "grammaticallyTransitive" : "transitive"]
: rawConjugations;
const form = getTenseVerbForm(conjugations, getTenseFromVerbSelection(VS), VS.voice);
const form = getTenseVerbForm(conjugations, getTenseFromVerbSelection(VS), VS.voice, VS.negative);
return <div className="mb-4">
<VerbFormDisplay
displayForm={form}

View File

@ -4,7 +4,8 @@ import {
} from "../np-picker/picker-tools";
import * as T from "../../types";
import ButtonSelect from "../ButtonSelect";
import { isModalTense, isPerfectTense, isVerbTense } from "../../lib/type-predicates";
import { isImperativeTense, isModalTense, isPerfectTense, isVerbTense } from "../../lib/type-predicates";
import { ensure2ndPersSubjPronounAndNoConflict } from "../../lib/phrase-building/vp-tools";
const verbTenseOptions: { label: string | JSX.Element, value: T.VerbTense }[] = [{
label: <div><i className="fas fa-video mr-2" />present</div>,
@ -55,8 +56,16 @@ const perfectTenseOptions: { label: string | JSX.Element, value: T.PerfectTense
value: "pastSubjunctivePerfect",
}];
export function getRandomTense(o?: T.PerfectTense | T.VerbTense | T.ModalTense): T.PerfectTense | T.VerbTense | T.ModalTense {
let tns: T.PerfectTense | T.VerbTense | T.ModalTense;
const imperativeTenseOptions: { label: string | JSX.Element, value: T.ImperativeTense }[] = [{
label: <div><i className="fas fa-video mr-2" />imperfective imp.</div>,
value: "imperfectiveImperative",
}, {
label: <div><i className="fas fa-camera mr-2" />perfective imp.</div>,
value: "perfectiveImperative",
}];
export function getRandomTense(o?: T.PerfectTense | T.VerbTense | T.ModalTense | T.ImperativeTense): T.PerfectTense | T.VerbTense | T.ModalTense | T.ImperativeTense {
let tns: T.PerfectTense | T.VerbTense | T.ModalTense | T.ImperativeTense;
const oldTenseCategory = !o
? undefined
: getTenseCategory(o);
@ -64,6 +73,8 @@ export function getRandomTense(o?: T.PerfectTense | T.VerbTense | T.ModalTense):
? perfectTenseOptions
: oldTenseCategory === "modal"
? verbTenseOptions.map(x => ({ ...x, value: `${x.value}Modal` as T.ModalTense }))
: oldTenseCategory === "imperative"
? imperativeTenseOptions
: verbTenseOptions;
do {
tns = tenseOptions[
@ -81,7 +92,7 @@ function TensePicker(props: ({
onChange: (p: T.VPSelection) => void,
mode: "charts" | "phrases" | "quiz",
}) {
function onTenseSelect(o: { value: T.VerbTense | T.PerfectTense } | null) {
function onTenseSelect(o: { value: T.VerbTense | T.PerfectTense | T.ImperativeTense } | null) {
if ("vpsComplete" in props) return;
const value = o?.value ? o.value : undefined;
if (props.vps.verb && value) {
@ -94,6 +105,15 @@ function TensePicker(props: ({
tenseCategory: "perfect",
},
});
} else if (isImperativeTense(value)) {
props.onChange({
...props.vps,
verb: {
...props.vps.verb,
imperativeTense: value,
tenseCategory: "imperative",
},
});
} else {
props.onChange({
...props.vps,
@ -112,9 +132,19 @@ function TensePicker(props: ({
if ("vpsComplete" in props) return;
if (!props.vps.verb) return;
return () => {
const tenses = props.vps.verb.tenseCategory === "perfect" ? perfectTenseOptions : verbTenseOptions;
// TODO: ABSTRACT THIS - SAFER
const tenses = props.vps.verb.tenseCategory === "perfect"
? perfectTenseOptions
: props.vps.verb.tenseCategory === "imperative"
? imperativeTenseOptions
: verbTenseOptions;
const currIndex = tenses.findIndex(tn => tn.value === props.vps.verb[
props.vps.verb.tenseCategory === "perfect" ? "perfectTense" : "verbTense"
// TODO: ABSTRACT THIS? - SAFER
props.vps.verb.tenseCategory === "perfect"
? "perfectTense"
: props.vps.verb.tenseCategory === "imperative"
? "imperativeTense"
: "verbTense"
]);
if (currIndex === -1) {
console.error("error moving tense", dir);
@ -139,9 +169,19 @@ function TensePicker(props: ({
});
}
}
function onTenseCategorySelect(value: "basic" | "modal" | "perfect") {
function onTenseCategorySelect(value: "basic" | "modal" | "perfect" | "imperative") {
if ("vpsComplete" in props) return;
if (props.vps.verb) {
if (value === "imperative") {
props.onChange(ensure2ndPersSubjPronounAndNoConflict({
...props.vps,
verb: {
...props.vps.verb,
tenseCategory: value,
},
}));
return;
}
props.onChange({
...props.vps,
verb: {
@ -153,40 +193,47 @@ function TensePicker(props: ({
}
const tOptions = ("vps" in props && (props.vps.verb?.tenseCategory === "perfect"))
? perfectTenseOptions
: ("vps" in props && (props.vps.verb?.tenseCategory === "imperative"))
? imperativeTenseOptions
: verbTenseOptions;
return <div>
<div style={{ maxWidth: "300px", minWidth: "250px", margin: "0 auto" }}>
<div className="d-flex flex-row justify-content-between align-items-center">
<div className="h5">Tense:</div>
{("vpsComplete" in props || props.vps.verb) && <div className="mb-2">
<ButtonSelect
small
value={"vpsComplete" in props
? getTenseCategory(props.vpsComplete.verb.tense)
: props.vps.verb.tenseCategory}
options={[{
label: "Basic",
value: "basic",
}, {
label: "Perfect",
value: "perfect",
}, {
label: "Modal",
value: "modal",
}]}
handleChange={props.mode !== "quiz" ? onTenseCategorySelect : () => null}
/>
</div>}
</div>
<div className="h5">Tense:</div>
{("vpsComplete" in props || props.vps.verb) && <div className="mb-2">
<ButtonSelect
small
value={"vpsComplete" in props
? getTenseCategory(props.vpsComplete.verb.tense)
: props.vps.verb.tenseCategory}
options={[{
label: "Basic",
value: "basic",
}, {
label: "Perfect",
value: "perfect",
}, {
label: "Modal",
value: "modal",
}, {
label: "Imperative",
value: "imperative",
}]}
handleChange={props.mode !== "quiz" ? onTenseCategorySelect : () => null}
/>
</div>}
{"vpsComplete" in props
? <div style={{ fontSize: "larger" }} className="mb-3">
{[...verbTenseOptions, ...perfectTenseOptions].find(o => o.value === props.vpsComplete.verb.tense)?.label}
{[...verbTenseOptions, ...perfectTenseOptions, ...imperativeTenseOptions].find(o => o.value === props.vpsComplete.verb.tense)?.label}
</div>
: <Select
isSearchable={false}
// for some reason can't use tOptions with find here;
value={props.vps.verb && ([...verbTenseOptions, ...perfectTenseOptions].find(o => o.value === props.vps.verb[
props.vps.verb.tenseCategory === "perfect" ? "perfectTense" : "verbTense"
value={props.vps.verb && ([...verbTenseOptions, ...perfectTenseOptions, ...imperativeTenseOptions].find(o => o.value === props.vps.verb[
props.vps.verb.tenseCategory === "perfect"
? "perfectTense"
: props.vps.verb.tenseCategory === "imperative"
? "imperativeTense"
: "verbTense"
]))}
onChange={onTenseSelect}
className="mb-2"
@ -219,7 +266,7 @@ function TensePicker(props: ({
export default TensePicker;
function getTenseCategory(tense: T.VerbTense | T.PerfectTense | T.ModalTense): "basic" | "perfect" | "modal" {
function getTenseCategory(tense: T.VerbTense | T.PerfectTense | T.ModalTense | T.ImperativeTense): "basic" | "perfect" | "modal" | "imperative" {
if (isPerfectTense(tense)) {
return "perfect";
}
@ -229,5 +276,8 @@ function getTenseCategory(tense: T.VerbTense | T.PerfectTense | T.ModalTense): "
if (isModalTense(tense)) {
return "modal";
}
if (isImperativeTense(tense)) {
return "imperative";
}
throw new Error("can't catagorize tense");
}

View File

@ -17,6 +17,10 @@ import VPExplorerQuiz from "./VPExplorerQuiz";
import { switchSubjObj } from "../../lib/phrase-building/vp-tools";
import VPExplorerExplanationModal, { roleIcon } from "./VPExplorerExplanationModal";
// TO FINISH IMPERATIVE STUFF!!
// TODO: English Builders for imperatives
// TODO: Quiz with imperatives
// TODO: make answerFeedback emojis appear at random translate angles a little bit
// add energy drinks?
@ -48,7 +52,7 @@ export function VPExplorer(props: {
})) {
const [vps, setVps] = useStickyState<T.VPSelection>(
savedVps => makeVPSelectionState(props.verb, savedVps),
"vpsState2",
"vpsState5",
);
const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">(
savedMode => {
@ -129,10 +133,16 @@ export function VPExplorer(props: {
{ label: "Phrases", value: "phrases" },
{ label: "Quiz", value: "quiz" },
]}
handleChange={setMode}
handleChange={(x) => {
// TODO: remove this and implement the imperative in quiz
// if (x === "quiz") {
// }
setMode(x);
}}
/>
</div>
{(vps.verb && (typeof vps.verb.object === "object") && (vps.verb.isCompound !== "dynamic") && (mode === "phrases")) &&
{(vps.verb && (typeof vps.verb.object === "object") && (vps.verb.isCompound !== "dynamic") && (vps.verb.tenseCategory !== "imperative") &&(mode === "phrases")) &&
<div className="text-center mt-4">
<button onClick={handleSubjObjSwap} className="btn btn-sm btn-light">
<i className="fas fa-exchange-alt mr-2" /> subj/obj
@ -154,6 +164,7 @@ export function VPExplorer(props: {
nouns: props.nouns,
verbs: props.verbs,
}}
is2ndPersonPicker={vps.verb.tenseCategory === "imperative"}
np={vps.subject}
counterPart={vps.verb ? vps.verb.object : undefined}
onChange={handleSubjectChange}

View File

@ -16,6 +16,7 @@ import Keyframes from "../Keyframes";
import energyDrink from "./energy-drink.jpg";
import { flattenLengths } from "../../lib/phrase-building/compile-vp";
import { concatPsString } from "../../lib/p-text-helpers";
import { isImperativeTense } from "../../lib/type-predicates";
const correctEmoji = ["✅", '🤓', "✅", '😊', "🌹", "✅", "✅", '🥳', "👏", "✅", "💯", "😎", "✅", "👍"];
@ -384,6 +385,7 @@ function completeVPs(vps: T.VPSelection): T.VPSelectionComplete {
obj: oldObj,
}
);
const t = getTenseFromVerbSelection(vps.verb);
const verb: T.VerbSelectionComplete = {
...vps.verb,
object: (
@ -397,7 +399,7 @@ function completeVPs(vps: T.VPSelection): T.VPSelectionComplete {
person: obj,
}
: vps.verb.object,
tense: getTenseFromVerbSelection(vps.verb),
tense: isImperativeTense(t) ? "presentVerb" : t,
};
return {
...vps,

View File

@ -53,6 +53,7 @@ export function makeVPSelectionState(
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",
object,
transitivity,

View File

@ -103,6 +103,7 @@ const kedulStatModal: T.ModalContent = {
};
const kawulStatOrDynImperfectivePassive: T.AspectContentPassive = {
imperative: undefined,
nonImperative: [
[[{p: "کول کېږم", f: "kawul kéGum"}], [{p: "کول کېږو", f: "kawul kéGoo"}]],
[[{p: "کول کېږم", f: "kawul kéGum"}], [{p: "کول کېږو", f: "kawul kéGoo"}]],
@ -1303,6 +1304,7 @@ export const kawulStat: T.VerbConjugation = {
passive: {
imperfective: kawulStatOrDynImperfectivePassive,
perfective: {
imperative: undefined,
nonImperative: [
[[{p: "وکړل شم", f: "óokRul shum"}], [{p: "وکړل شو", f: "óokRul shoo"}]],
[[{p: "وکړل شم", f: "óokRul shum"}], [{p: "وکړل شو", f: "óokRul shoo"}]],
@ -1808,6 +1810,7 @@ export const kawulDyn: T.VerbConjugation = {
passive: {
imperfective: kawulStatOrDynImperfectivePassive,
perfective: {
imperative: undefined,
nonImperative: [
[[{p: "کړل شم", f: "kRul shum"}], [{p: "کړل شو", f: "kRul shoo"}]],
[[{p: "کړل شم", f: "kRul shum"}], [{p: "کړل شو", f: "kRul shoo"}]],

View File

@ -12,7 +12,7 @@ import {
removeBa,
removeDuplicates,
} from "./vp-tools";
import { isModalTense, isPerfectTense } from "../type-predicates";
import { isImperativeTense, isModalTense, isPerfectTense } from "../type-predicates";
type Form = T.FormVersion & { OSV?: boolean };
export function compileVP(VP: T.VPRendered, form: Form): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] };
@ -159,7 +159,9 @@ function arrangeVerbWNegative(head: T.PsString | undefined, restRaw: T.PsString[
headSegment ? [headSegment, rest] : [rest],
];
}
const nu: T.PsString = { p: "نه", f: "nú" };
const nu: T.PsString = isImperativeTense(V.tense)
? { p: "مه", f: "mú" }
: { p: "نه", f: "nú" };
if (!headSegment) {
if ("front" in rest) {
return [

View File

@ -2,6 +2,7 @@ import * as T from "../../types";
import { getVerbBlockPosFromPerson, parseEc } from "../misc-helpers";
import * as grammarUnits from "../grammar-units";
import {
isImperativeTense,
isPerfectTense,
isVerbTense,
// isModalTense,
@ -232,11 +233,24 @@ export function renderEnglishVPBase({ subjectPerson, object, vs }: {
`$SUBJ would${n ? " not" : ""} be able to be ${v[4]}`,
]),
};
const imperativeBuilders: Record<
T.ImperativeTense,
(s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => string[]
> = {
imperfectiveImperative: (s: T.Person, ec: T.EnglishVerbConjugationEc, n: boolean) => ([
`${n ? "don't " : ""}${ec[0]}`,
]),
perfectiveImperative: (s: T.Person, ec: T.EnglishVerbConjugationEc, n: boolean) => ([
`${n ? "don't " : ""}${ec[0]}`,
]),
};
const base = (
isPerfectTense(vs.tense)
? (vs.voice === "active" ? perfectBuilders : passivePerfectBuilders)[vs.tense]
: isVerbTense(vs.tense)
? (vs.voice === "active" ? basicBuilders : passiveBasicBuilders)[vs.tense]
: isImperativeTense(vs.tense)
? imperativeBuilders[vs.tense]
: (vs.voice === "active" ? modalBuilders : passiveModalBuilders)[vs.tense])(subjectPerson, ec, vs.negative);
return base.map(b => `${b}${typeof object === "object" ? " $OBJ" : ""}${ep ? ` ${ep}` : ""}`);
}

View File

@ -13,6 +13,7 @@ import {
hasBaParticle,
psStringFromEntry,
getLong,
isImperativeBlock,
} from "../p-text-helpers";
import { removeAccents } from "../accent-helpers";
import {
@ -26,6 +27,7 @@ import {
isPerfectTense,
} from "../type-predicates";
import { renderEnglishVPBase } from "./english-vp-rendering";
import { personGender } from "../../library";
// TODO: ISSUE GETTING SPLIT HEAD NOT MATCHING WITH FUTURE VERBS
@ -157,7 +159,8 @@ function getPsVerbConjugation(conj: T.VerbConjugation, vs: T.VerbSelectionComple
},
hasBa: boolean,
} {
const f = getTenseVerbForm(conj, vs.tense, vs.voice);
// TODO: handle the imperative form here
const f = getTenseVerbForm(conj, vs.tense, vs.voice, vs.negative);
const block = getMatrixBlock(f, objectPerson, person);
const perfective = isPerfective(vs.tense);
const verbForm = getVerbFromBlock(block, person);
@ -178,8 +181,11 @@ function getPsVerbConjugation(conj: T.VerbConjugation, vs: T.VerbSelectionComple
return { hasBa, ps: { head: undefined, rest: verbForm }};
}
function getVerbFromBlock(block: T.SingleOrLengthOpts<T.VerbBlock>, person: T.Person): T.SingleOrLengthOpts<T.PsString[]> {
function grabFromBlock(b: T.VerbBlock, [row, col]: [ row: number, col: number ]): T.PsString[] {
function getVerbFromBlock(block: T.SingleOrLengthOpts<T.VerbBlock | T.ImperativeBlock>, person: T.Person): T.SingleOrLengthOpts<T.PsString[]> {
function grabFromBlock(b: T.VerbBlock | T.ImperativeBlock, [row, col]: [ row: number, col: number ]): T.PsString[] {
if (isImperativeBlock(b)) {
return b[personGender(person) === "masc" ? 0 : 1][col];
}
return b[row][col];
}
const pos = getVerbBlockPosFromPerson(person);
@ -326,7 +332,7 @@ function isFirstOrSecondPersPronoun(o: "none" | T.NPSelection | T.Person.ThirdPl
return [0,1,2,3,6,7,8,9].includes(o.person);
}
function isPerfective(t: T.VerbTense | T.PerfectTense | T.ModalTense): boolean {
function isPerfective(t: T.Tense): boolean {
if (isPerfectTense(t)) return false;
if (t === "presentVerb" || t === "imperfectiveFuture" || t === "imperfectivePast" || t === "habitualImperfectivePast") {
return false;
@ -337,7 +343,10 @@ function isPerfective(t: T.VerbTense | T.PerfectTense | T.ModalTense): boolean {
if (t === "perfectiveFutureModal" || t === "subjunctiveVerbModal" || t === "perfectivePastModal" || t === "habitualPerfectivePastModal") {
return true;
}
throw new Error("tense not implemented yet");
if (t === "perfectiveImperative") {
return true;
}
return false;
}
function isMascSingAnimatePattern4(np: T.NPSelection): boolean {

View File

@ -4,8 +4,9 @@ import {
psRemove,
psStringEquals,
} from "../../lib/p-text-helpers";
import { isPerfectTense } from "../type-predicates";
import { isImperativeTense, isPerfectTense } from "../type-predicates";
import * as grammarUnits from "../../lib/grammar-units";
import { randomNumber } from "../../lib/misc-helpers";
export function isInvalidSubjObjCombo(subj: T.Person, obj: T.Person): boolean {
const firstPeople = [
@ -27,8 +28,23 @@ export function isInvalidSubjObjCombo(subj: T.Person, obj: T.Person): boolean {
);
}
export function getTenseVerbForm(conjR: T.VerbConjugation, tense: T.VerbTense | T.PerfectTense | T.ModalTense, voice: "active" | "passive"): T.VerbForm {
export function getTenseVerbForm(
conjR: T.VerbConjugation,
tense: T.VerbTense | T.PerfectTense | T.ModalTense | T.ImperativeTense,
voice: "active" | "passive",
negative: boolean,
): T.VerbForm | T.ImperativeForm {
const conj = (voice === "passive" && conjR.passive) ? conjR.passive : conjR;
if (isImperativeTense(tense)) {
const impPassError = new Error("can't use imperative tenses with passive voice")
if (voice === "passive") {
throw impPassError;
}
if (!conj.imperfective.imperative || !conj.perfective.imperative) throw impPassError;
return (tense === "perfectiveImperative" && !negative)
? conj.perfective.imperative
: conj.imperfective.imperative;
}
if (tense === "presentVerb") {
return conj.imperfective.nonImperative;
}
@ -123,7 +139,7 @@ export function removeBa(ps: T.PsString): T.PsString {
return psRemove(ps, concatPsString(grammarUnits.baParticle, " "));
}
export function getTenseFromVerbSelection(vs: T.VerbSelection): T.VerbTense | T.PerfectTense | T.ModalTense {
export function getTenseFromVerbSelection(vs: T.VerbSelection): T.VerbTense | T.PerfectTense | T.ModalTense | T.ImperativeTense {
function verbTenseToModalTense(tn: T.VerbTense): T.ModalTense {
if (tn === "presentVerb") {
return "presentVerbModal";
@ -157,11 +173,14 @@ export function getTenseFromVerbSelection(vs: T.VerbSelection): T.VerbTense | T.
if (vs.tenseCategory === "perfect") {
return vs.perfectTense;
}
if (vs.tenseCategory === "imperative") {
return vs.imperativeTense;
}
// vs.tenseCategory === "modal"
return verbTenseToModalTense(vs.verbTense);
}
export function isPastTense(tense: T.VerbTense | T.PerfectTense | T.ModalTense): boolean {
export function isPastTense(tense: T.Tense): boolean {
if (isPerfectTense(tense)) return true;
return tense.toLowerCase().includes("past");
}
@ -178,7 +197,7 @@ export function switchSubjObj(vps: T.VPSelection): T.VPSelection;
export function switchSubjObj(vps: T.VPSelectionComplete): T.VPSelectionComplete;
export function switchSubjObj(vps: T.VPSelection | T.VPSelectionComplete): T.VPSelection | T.VPSelectionComplete {
if ("tenseCategory" in vps.verb) {
if (!vps.subject || !(typeof vps.verb.object === "object")) {
if (!vps.subject || !(typeof vps.verb.object === "object") || (vps.verb.tenseCategory === "imperative")) {
return vps;
}
return {
@ -222,4 +241,91 @@ export function completeVPSelection(vps: T.VPSelection): T.VPSelectionComplete |
subject,
verb,
};
}
export function isSecondPerson(p: T.Person): boolean {
return (
p === T.Person.SecondSingMale ||
p === T.Person.SecondSingFemale ||
p === T.Person.SecondPlurMale ||
p === T.Person.SecondPlurFemale
);
}
export function isThirdPerson(p: T.Person): boolean {
return (
p === T.Person.ThirdSingMale ||
p === T.Person.ThirdSingFemale ||
p === T.Person.ThirdPlurMale ||
p === T.Person.ThirdPlurFemale
);
}
export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelection): T.VPSelection {
console.log("checking more...", vps);
const subjIs2ndPerson = (vps.subject?.type === "pronoun") && isSecondPerson(vps.subject.person);
const objIs2ndPerson = (typeof vps.verb.object === "object")
&& (vps.verb.object.type === "pronoun")
&& isSecondPerson(vps.verb.object.person);
console.log({ subjIs2ndPerson, objIs2ndPerson });
const default2ndPersSubject: T.PronounSelection = {
type: "pronoun",
distance: "far",
person: T.Person.SecondSingMale,
};
function getNon2ndPersPronoun() {
let newObjPerson: T.Person;
do {
newObjPerson = randomNumber(0, 12);
} while(isSecondPerson(newObjPerson));
return newObjPerson;
}
if (subjIs2ndPerson && !objIs2ndPerson) {
return vps;
}
if (subjIs2ndPerson && objIs2ndPerson) {
if (typeof vps.verb.object !== "object" || vps.verb.object.type !== "pronoun") {
return vps;
}
return {
...vps,
verb: {
...vps.verb,
object: {
...vps.verb.object,
person: getNon2ndPersPronoun(),
},
},
};
}
if (!subjIs2ndPerson && objIs2ndPerson) {
if (typeof vps.verb.object !== "object" || vps.verb.object.type !== "pronoun") {
return {
...vps,
subject: default2ndPersSubject,
};
}
return {
...vps,
subject: default2ndPersSubject,
verb: {
...vps.verb,
object: {
...vps.verb.object,
person: getNon2ndPersPronoun(),
},
},
};
}
if (!subjIs2ndPerson && !objIs2ndPerson) {
console.log("returning last");
return {
...vps,
subject: default2ndPersSubject,
verb: {
...vps.verb,
},
};
}
throw new Error("error ensuring compatible VPSelection for imperative verb");
}

View File

@ -160,11 +160,11 @@ export function isArrayOneOrMore<U>(a: U[]): a is T.ArrayOneOrMore<U> {
return a.length > 0;
}
export function isPerfectTense(tense: T.VerbTense | T.EquativeTense | T.ModalTense | T.PerfectTense): tense is T.PerfectTense {
export function isPerfectTense(tense: T.Tense): tense is T.PerfectTense {
return tense.endsWith("Perfect");
}
export function isVerbTense(tense: T.VerbTense | T.EquativeTense | T.ModalTense | T.PerfectTense): tense is T.VerbTense {
export function isVerbTense(tense: T.Tense): tense is T.VerbTense {
const verbTenses: T.VerbTense[] = [
"presentVerb",
"subjunctiveVerb",
@ -178,12 +178,15 @@ export function isVerbTense(tense: T.VerbTense | T.EquativeTense | T.ModalTense
return verbTenses.some(x => x === tense);
}
export function isModalTense(tense: T.VerbTense | T.EquativeTense | T.ModalTense | T.PerfectTense): tense is T.ModalTense {
export function isModalTense(tense: T.Tense): tense is T.ModalTense {
return tense.endsWith("Modal");
}
export function isEquativeTense(t: T.VerbTense | T.EquativeTense | T.PerfectTense | T.ModalTense): t is T.EquativeTense {
export function isEquativeTense(t: T.Tense): t is T.EquativeTense {
return (t === "present" || t === "future" || t === "habitual" || t === "past" || t === "wouldBe" || t === "subjunctive" || t === "pastSubjunctive");
}
export function isImperativeTense(tense: T.Tense): tense is T.ImperativeTense {
return tense === "imperfectiveImperative" || tense === "perfectiveImperative";
}

View File

@ -106,11 +106,11 @@ export function conjugateVerb(entry: T.DictionaryEntry, complement?: T.Dictionar
}
function conjugateDynamicCompound(info: T.DynamicCompoundVerbInfo): T.VerbConjugation {
const willUseImperative = !(
info.type === "dynamic compound"
&& info.transitivity === "intransitive"
&& info.auxVerb.p === "کېدل"
);
// const willUseImperative = !(
// info.type === "dynamic compound"
// && info.transitivity === "intransitive"
// && info.auxVerb.p === "کېدل"
// );
const auxConj = enforceObject(
conjugateVerb(info.auxVerb, info.auxVerbComplement) as T.VerbConjugation,
info.objComplement.person,
@ -136,18 +136,14 @@ function conjugateDynamicCompound(info: T.DynamicCompoundVerbInfo): T.VerbConjug
const ac = auxConj[aspect];
const nonImperative = addToForm([complement, " "], ac.nonImperative);
const future = addToForm([baParticle, " "], nonImperative);
const imperative = (ac.imperative && willUseImperative)
? addToForm([complement, " "], ac.imperative)
: null;
const imperative = addToForm([complement, " "], ac.imperative);
const past = addToForm([complement, " "], auxConj[aspect].past);
const habitualPast = addToForm([baParticle, " "], past);
const modal = makeDynamicModalContent();
return {
nonImperative,
future,
...imperative ? {
imperative,
} : {},
imperative,
past,
habitualPast,
modal,
@ -183,6 +179,7 @@ function conjugateDynamicCompound(info: T.DynamicCompoundVerbInfo): T.VerbConjug
const habitualPast = addToForm([baParticle, " "], past);
const modal = makePassiveModalSection([complement, " "], stativeAux.intransitive.imperfective.modal);
return {
imperative: undefined,
nonImperative,
future,
past,
@ -317,9 +314,7 @@ function makeStativeCompoundSeperatedAspectContent(info: T.StativeCompoundVerbIn
stativeAux[transitivity][aspect].nonImperative,
);
const future = addToForm([baParticle, " "], nonImperative);
const imperative = aux.imperative
? addToForm([presentComplement, " "], aux.imperative)
: null;
const imperative = addToForm([presentComplement, " "], aux.imperative);
const past = addToForm([info.complement, " "], aux.past);
const habitualPast = addToForm([baParticle, " "], past);
return {
@ -327,9 +322,7 @@ function makeStativeCompoundSeperatedAspectContent(info: T.StativeCompoundVerbIn
future,
past,
habitualPast,
...imperative ? {
imperative,
} : {},
imperative,
modal: info.transitivity === "transitive"
? makeTransitiveStativeModalContent()
: makeJoinedModalContent(info, "imperfective"),
@ -458,6 +451,7 @@ function makePassiveContent(info: T.NonComboVerbInfo): {
noPersInfs(info.root.imperfective).long, " ",
], stativeAux.intransitive.imperfective.modal);
return {
imperative: undefined,
nonImperative,
future,
past,
@ -474,6 +468,7 @@ function makePassiveContent(info: T.NonComboVerbInfo): {
const auxModal = aux.modal;
const modal = makePassiveModalSection([noPersInfs(info.root.imperfective).long, " "], auxModal);
return {
imperative: undefined,
nonImperative, // ROOT LONG + kedulStat[aspect].nonImperative
future, // به ba + ROOT LONG + this.nonImperative
past, // ROOT LONG + kedulStat[aspect].past
@ -560,9 +555,7 @@ function enforceObject(conj: T.VerbConjugation, person: T.Person): T.VerbConjuga
const modifyPastInAspect = (as: T.AspectContent): T.AspectContent => ({
nonImperative: allOnePersonInflection(as.nonImperative, person),
future: allOnePersonInflection(as.future, person),
...as.imperative ? {
imperative: allOnePersonInflection(as.imperative, person),
} : {},
imperative: allOnePersonInflection(as.imperative, person),
past: allOnePersonVerbForm(as.past, person),
habitualPast: allOnePersonInflection(as.habitualPast, person),
modal: {
@ -586,6 +579,7 @@ function enforceObject(conj: T.VerbConjugation, person: T.Person): T.VerbConjuga
pastSubjunctiveHypothetical: allOnePersonVerbForm(perf.pastSubjunctiveHypothetical, person),
});
const modifyPassiveAspect = (as: T.AspectContentPassive): T.AspectContentPassive => ({
imperative: undefined,
nonImperative: allOnePersonVerbForm(as.nonImperative, person),
future: allOnePersonVerbForm(as.future, person),
past: allOnePersonVerbForm(as.past, person),

View File

@ -296,13 +296,17 @@ export type AspectContent = {
// ROOT = info.root[ASPECT]
nonImperative: VerbForm; // STEM + pres ending
future: VerbForm; // به + this.nonImperative
imperative?: ImperativeForm; // STEM + imperative ending
// -- optional because not used for intransitive verison of kawul dynamic compounds
imperative: ImperativeForm; // STEM + imperative ending
past: VerbForm; // ROOT + past ending
habitualPast: VerbForm; // ba + past
modal: ModalContent;
}
// ASPECT -> AspectContentPssive
export type AspectContentPassive = Omit<AspectContent, "imperative"> & {
imperative: undefined,
};
export type ModalContent = {
nonImperative: VerbForm; // ROOT + ey + kedulStat.perfective.nonImperative
future: VerbForm; // به + this.nonImperative
@ -311,16 +315,6 @@ export type ModalContent = {
hypotheticalPast: VerbForm; // ROOT + ey + shw + ey
}
// ASPECT -> AspectContentPssive
export type AspectContentPassive = {
// ROOT = info.root[ASPECT]
nonImperative: VerbForm; // ROOT LONG + kedulStat[ASPECT].nonImperative
future: VerbForm; // ba + this.nonImperative
past: VerbForm; // ROOT LONG + kedulStat[ASPECT].past
habitualPast: VerbForm;
modal: ModalContent,
}
export type ParticipleForm = SingleOrLengthOpts<UnisexInflections> | SingleOrLengthOpts<PsString>;
export type ParticipleContent = {
@ -531,12 +525,13 @@ export type VerbTense = "presentVerb"
| "imperfectivePast"
| "habitualPerfectivePast"
| "habitualImperfectivePast";
export type EquativeTense = "present" | "subjunctive" | "habitual" | "past" | "future" | "wouldBe" | "pastSubjunctive";
export type NounNumber = "singular" | "plural";
export type EquativeTense = "present" | "subjunctive" | "habitual" | "past" | "future" | "wouldBe" | "pastSubjunctive";
export type PerfectTense = `${EquativeTense}Perfect`;
export type ModalTense = `${VerbTense}Modal`;
export type ImperativeTense = `${Aspect}Imperative`;
export type Tense = EquativeTense | VerbTense | PerfectTense | ModalTense | ImperativeTense;
export type VPSelection = {
subject: NPSelection | undefined,
@ -550,7 +545,7 @@ export type VPSelectionComplete = {
export type VerbSelectionComplete = Omit<VerbSelection, "object" | "verbTense" | "perfectTense" | "tenseCategory"> & {
object: Exclude<VerbObject, undefined>,
tense: VerbTense | PerfectTense | ModalTense,
tense: VerbTense | PerfectTense | ModalTense | ImperativeTense,
}
export type VerbSelection = {
@ -569,7 +564,8 @@ export type VerbSelection = {
negative: boolean,
verbTense: VerbTense,
perfectTense: PerfectTense,
tenseCategory: "basic" | "modal" | "perfect",
imperativeTense: ImperativeTense,
tenseCategory: "basic" | "modal" | "perfect" | "imperative",
};
export type VerbRendered = Omit<VerbSelectionComplete, "object"> & {