From 875237439b3b5fdfb2bba3588821d931432cb778 Mon Sep 17 00:00:00 2001 From: lingdocs <71590811+lingdocs@users.noreply.github.com> Date: Tue, 19 Apr 2022 22:56:02 +0500 Subject: [PATCH] better UI and refactor re tenseCategory --- package.json | 2 +- src/components/vp-explorer/ChartDisplay.tsx | 7 +- src/components/vp-explorer/TensePicker.tsx | 153 +++++++------ src/components/vp-explorer/VPDisplay.tsx | 15 +- src/components/vp-explorer/VPExplorer.tsx | 7 +- src/components/vp-explorer/VPExplorerQuiz.tsx | 59 ++++- src/components/vp-explorer/verb-selection.ts | 20 +- src/lib/pashto-inflector.test.ts | 16 ++ src/lib/phrase-building/compile-vp.ts | 3 +- .../phrase-building/english-vp-rendering.ts | 81 +++---- src/lib/phrase-building/render-vp.ts | 17 +- src/lib/phrase-building/vp-tools.ts | 214 +++++++++++------- src/lib/type-predicates.ts | 31 ++- src/types.ts | 21 +- 14 files changed, 404 insertions(+), 242 deletions(-) diff --git a/package.json b/package.json index 7ddff84..9fe489e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lingdocs/pashto-inflector", - "version": "2.1.1", + "version": "2.1.2", "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", diff --git a/src/components/vp-explorer/ChartDisplay.tsx b/src/components/vp-explorer/ChartDisplay.tsx index 34ccd03..c610798 100644 --- a/src/components/vp-explorer/ChartDisplay.tsx +++ b/src/components/vp-explorer/ChartDisplay.tsx @@ -1,4 +1,7 @@ -import { getTenseVerbForm } from "../../lib/phrase-building/vp-tools"; +import { + getTenseVerbForm, + getTenseFromVerbSelection, +} from "../../lib/phrase-building/vp-tools"; import VerbFormDisplay from "../VerbFormDisplay"; import { conjugateVerb } from "../../lib/verb-conjugation"; import * as T from "../../types"; @@ -13,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, VS.tense, VS.tenseCategory, VS.voice); + const form = getTenseVerbForm(conjugations, getTenseFromVerbSelection(VS), VS.voice); return
present
, @@ -34,32 +34,37 @@ const verbTenseOptions: { label: string | JSX.Element, value: T.VerbTense }[] = const perfectTenseOptions: { label: string | JSX.Element, value: T.PerfectTense }[] = [{ label: "Present Perfect", - value: "present perfect", + value: "presentPerfect", }, { label: "Habitual Perfect", - value: "habitual perfect", + value: "habitualPerfect", }, { label: "Subjunctive Perfect", - value: "subjunctive perfect", + value: "subjunctivePerfect", }, { label: "Future Perfect", - value: "future perfect", + value: "futurePerfect", }, { label: "Past Perfect", - value: "past perfect", + value: "pastPerfect", }, { label: `"Would Be" Perfect`, - value: "wouldBe perfect", + value: "wouldBePerfect", }, { label: "Past Subjunctive Perfect", - value: "pastSubjunctive perfect", + value: "pastSubjunctivePerfect", }]; -export function getRandomTense(type: "basic" | "modal", o?: T.VerbTense): T.VerbTense; -export function getRandomTense(type: "perfect", o?: T.PerfectTense | T.VerbTense): T.PerfectTense; -export function getRandomTense(type: "basic" | "modal" | "perfect", o?: T.PerfectTense | T.VerbTense): T.PerfectTense | T.VerbTense { - let tns: T.PerfectTense | T.VerbTense; - const tenseOptions = type === "perfect" ? perfectTenseOptions : verbTenseOptions; +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 oldTenseCategory = !o + ? undefined + : getTenseCategory(o); + const tenseOptions = oldTenseCategory === "perfect" + ? perfectTenseOptions + : oldTenseCategory === "modal" + ? verbTenseOptions.map(x => ({ ...x, value: `${x.value}Modal` as T.ModalTense })) + : verbTenseOptions; do { tns = tenseOptions[ Math.floor(Math.random()*tenseOptions.length) @@ -68,40 +73,49 @@ export function getRandomTense(type: "basic" | "modal" | "perfect", o?: T.Perfec return tns; } -function TensePicker({ onChange, vps, mode }: { +function TensePicker(props: ({ vps: T.VPSelection, +} | { + vpsComplete: T.VPSelectionComplete, +}) & { onChange: (p: T.VPSelection) => void, mode: "charts" | "phrases" | "quiz", }) { function onTenseSelect(o: { value: T.VerbTense | T.PerfectTense } | null) { - const value = o?.value ? o.value : undefined; - if (vps.verb && value) { + if ("vpsComplete" in props) return; + const value = o?.value ? o.value : undefined; + if (props.vps.verb && value) { if (isPerfectTense(value)) { - onChange({ - ...vps, + props.onChange({ + ...props.vps, verb: { - ...vps.verb, - tense: value, + ...props.vps.verb, + perfectTense: value, tenseCategory: "perfect", }, }); } else { - onChange({ - ...vps, + props.onChange({ + ...props.vps, verb: { - ...vps.verb, - tense: value, - tenseCategory: vps.verb.tenseCategory === "perfect" ? "basic" : vps.verb.tenseCategory, + ...props.vps.verb, + verbTense: value, + tenseCategory: props.vps.verb.tenseCategory === "perfect" + ? "basic" + : props.vps.verb.tenseCategory, }, }); } } } function moveTense(dir: "forward" | "back") { - if (!vps.verb) return; + if ("vpsComplete" in props) return; + if (!props.vps.verb) return; return () => { - const tenses = vps.verb.tenseCategory === "perfect" ? perfectTenseOptions : verbTenseOptions; - const currIndex = tenses.findIndex(tn => tn.value === vps.verb.tense) + const tenses = props.vps.verb.tenseCategory === "perfect" ? perfectTenseOptions : verbTenseOptions; + const currIndex = tenses.findIndex(tn => tn.value === props.vps.verb[ + props.vps.verb.tenseCategory === "perfect" ? "perfectTense" : "verbTense" + ]); if (currIndex === -1) { console.error("error moving tense", dir); return; @@ -114,48 +128,42 @@ function TensePicker({ onChange, vps, mode }: { }; } function onPosNegSelect(value: string) { - if (vps.verb) { - onChange({ - ...vps, + if ("vpsComplete" in props) return; + if (props.vps.verb) { + props.onChange({ + ...props.vps, verb: { - ...vps.verb, + ...props.vps.verb, negative: value === "true", }, }); } } function onTenseCategorySelect(value: "basic" | "modal" | "perfect") { - if (vps.verb) { - if (value === "perfect") { - onChange({ - ...vps, - verb: { - ...vps.verb, - tenseCategory: value, - tense: isPerfectTense(vps.verb.tense) ? vps.verb.tense : "present perfect", - }, - }); - } else { - onChange({ - ...vps, - verb: { - ...vps.verb, - tenseCategory: value, - tense: isPerfectTense(vps.verb.tense) ? "presentVerb" : vps.verb.tense, - } - }); - } + if ("vpsComplete" in props) return; + if (props.vps.verb) { + props.onChange({ + ...props.vps, + verb: { + ...props.vps.verb, + tenseCategory: value, + }, + }); } } - const tOptions = (vps.verb?.tenseCategory === "perfect") ? perfectTenseOptions : verbTenseOptions; + const tOptions = ("vps" in props && (props.vps.verb?.tenseCategory === "perfect")) + ? perfectTenseOptions + : verbTenseOptions; return
Tense:
- {vps.verb &&
+ {("vpsComplete" in props || props.vps.verb) &&
null} + handleChange={props.mode !== "quiz" ? onTenseCategorySelect : () => null} />
}
- o.value === vps.verb.tense))} + value={props.vps.verb && ([...verbTenseOptions, ...perfectTenseOptions].find(o => o.value === props.vps.verb[ + props.vps.verb.tenseCategory === "perfect" ? "perfectTense" : "verbTense" + ]))} onChange={onTenseSelect} className="mb-2" options={tOptions} {...zIndexProps} - /> - {vps.verb && (mode !== "quiz") &&
+ />} + {"vps" in props && props.vps.verb && (props.mode !== "quiz") &&
- {mode === "phrases" && ; } -export default TensePicker; \ No newline at end of file +export default TensePicker; + +function getTenseCategory(tense: T.VerbTense | T.PerfectTense | T.ModalTense): "basic" | "perfect" | "modal" { + if (isPerfectTense(tense)) { + return "perfect"; + } + if (isVerbTense(tense)) { + return "basic"; + } + if (isModalTense(tense)) { + return "modal"; + } + throw new Error("can't catagorize tense"); +} \ No newline at end of file diff --git a/src/components/vp-explorer/VPDisplay.tsx b/src/components/vp-explorer/VPDisplay.tsx index fa57878..b30df21 100644 --- a/src/components/vp-explorer/VPDisplay.tsx +++ b/src/components/vp-explorer/VPDisplay.tsx @@ -2,14 +2,17 @@ import { renderVP, compileVP } from "../../lib/phrase-building/index"; import * as T from "../../types"; import InlinePs from "../InlinePs"; import AbbreviationFormSelector from "./AbbreviationFormSelector"; -import { isPastTense } from "../../lib/phrase-building/vp-tools"; +import { + isPastTense, + completeVPSelection, +} from "../../lib/phrase-building/vp-tools"; import { useStickyState } from "../../library"; -import { isVPSelectionComplete } from "../../lib/type-predicates"; -function VPDisplay({ VP, opts }: { VP: T.VPSelection | T.VPSelectionComplete, opts: T.TextOptions }) { +function VPDisplay({ VP, opts }: { VP: T.VPSelection, opts: T.TextOptions }) { const [form, setForm] = useStickyState({ removeKing: false, shrinkServant: false }, "abbreviationForm"); const [OSV, setOSV] = useStickyState(false, "includeOSV"); - if (!isVPSelectionComplete(VP)) { + const VPComplete = completeVPSelection(VP); + if (!VPComplete) { return
{(() => { const twoNPs = (VP.subject === undefined) && (VP.verb.object === undefined); @@ -17,7 +20,7 @@ function VPDisplay({ VP, opts }: { VP: T.VPSelection | T.VPSelectionComplete, op })()}
; } - const result = compileVP(renderVP(VP), { ...form, OSV }); + const result = compileVP(renderVP(VPComplete), { ...form, OSV }); return
{VP.verb.transitivity === "transitive" &&
} diff --git a/src/components/vp-explorer/VPExplorer.tsx b/src/components/vp-explorer/VPExplorer.tsx index f587900..81826ae 100644 --- a/src/components/vp-explorer/VPExplorer.tsx +++ b/src/components/vp-explorer/VPExplorer.tsx @@ -48,7 +48,7 @@ export function VPExplorer(props: { })) { const [vps, setVps] = useStickyState( savedVps => makeVPSelectionState(props.verb, savedVps), - "vpsState1", + "vpsState2", ); const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">( savedMode => { @@ -59,7 +59,10 @@ export function VPExplorer(props: { "verbExplorerMode", ); const [showingExplanation, setShowingExplanation] = useState<{ role: "servant" | "king", item: "subject" | "object" } | false>(false); - const roles = getKingAndServant(isPastTense(vps.verb.tense), vps.verb.transitivity !== "intransitive"); + const roles = getKingAndServant( + isPastTense(vps.verb.tenseCategory === "perfect" ? vps.verb.perfectTense : vps.verb.verbTense), + vps.verb.transitivity !== "intransitive", + ); useEffect(() => { setVps(oldVps => { if (mode === "quiz") { diff --git a/src/components/vp-explorer/VPExplorerQuiz.tsx b/src/components/vp-explorer/VPExplorerQuiz.tsx index 88e69e2..ac5f229 100644 --- a/src/components/vp-explorer/VPExplorerQuiz.tsx +++ b/src/components/vp-explorer/VPExplorerQuiz.tsx @@ -9,7 +9,7 @@ import InlinePs from "../InlinePs"; import { psStringEquals } from "../../lib/p-text-helpers"; import { renderVP, compileVP } from "../../lib/phrase-building/index"; import { getRandomTense } from "./TensePicker"; -import { removeBa, switchSubjObj } from "../../lib/phrase-building/vp-tools"; +import { getTenseFromVerbSelection, removeBa, switchSubjObj } from "../../lib/phrase-building/vp-tools"; import playAudio from "../../lib/play-audio"; import TensePicker from "./TensePicker"; import Keyframes from "../Keyframes"; @@ -57,7 +57,7 @@ function VPExplorerQuiz(props: { opts: T.TextOptions, vps: T.VPSelection, }) { - const startingQs = tickQuizState(props.vps); + const startingQs = tickQuizState(completeVPs(props.vps)); const [quizState, setQuizState] = useState(startingQs); const [showCheck, setShowCheck] = useState(false); const [answerBlank, setAnswerBlank] = useState(""); @@ -115,7 +115,7 @@ function VPExplorerQuiz(props: {
}
null} mode={"quiz"} /> @@ -278,7 +278,7 @@ function QuizNPDisplay({ children, stage, opts }: { * @param startingWith * @returns */ -function tickQuizState(startingWith: T.VPSelection | QuizState): QuizState { +function tickQuizState(startingWith: T.VPSelectionComplete | QuizState): QuizState { function makeRes(x: T.VPSelectionComplete) { return compileVP(renderVP(x), { removeKing: false, shrinkServant: false }); } @@ -369,13 +369,54 @@ function getOptionFromResult(r: { return ps[0]; } +function completeVPs(vps: T.VPSelection): T.VPSelectionComplete { + const oldSubj = vps.subject?.type === "pronoun" + ? vps.subject.person + : undefined; + const oldObj = (typeof vps.verb.object === "object" && vps.verb.object.type === "pronoun") + ? vps.verb.object.person + : undefined; + const { subj, obj } = randomSubjObj( + oldSubj === undefined + ? undefined + : { + subj: oldSubj, + obj: oldObj, + } + ); + const verb: T.VerbSelectionComplete = { + ...vps.verb, + object: ( + (typeof vps.verb.object === "object" && !(vps.verb.object.type === "noun" && vps.verb.object.dynamicComplement)) + || + vps.verb.object === undefined + ) + ? { + type: "pronoun", + distance: "far", + person: obj, + } + : vps.verb.object, + tense: getTenseFromVerbSelection(vps.verb), + }; + return { + ...vps, + subject: { + type: "pronoun", + distance: "far", + person: subj, + }, + verb, + }; +} + function getRandomVPSelection(mix: MixType = "both") { // TODO: Type safety to make sure it's safe? - return ({ subject, verb }: T.VPSelection): T.VPSelectionComplete => { - const oldSubj = (subject?.type === "pronoun") + return ({ subject, verb }: T.VPSelectionComplete): T.VPSelectionComplete => { + const oldSubj = (subject.type === "pronoun") ? subject.person : undefined; - const oldObj = (typeof verb?.object === "object" && verb.object.type === "pronoun") + const oldObj = (typeof verb.object === "object" && verb.object.type === "pronoun") ? verb.object.person : undefined; const { subj, obj } = randomSubjObj( @@ -401,7 +442,6 @@ function getRandomVPSelection(mix: MixType = "both") { if (mix === "tenses") { return { subject: subject !== undefined ? subject : randSubj, - // @ts-ignore verb: randomizeTense(verb, true), } } @@ -426,9 +466,6 @@ function randomizeTense(verb: T.VerbSelectionComplete, dontRepeatTense: boolean) return { ...verb, tense: getRandomTense( - // TODO: WHY ISN'T THE OVERLOADING ON THIS - // @ts-ignore - verb.tenseCategory, dontRepeatTense ? verb.tense : undefined, ), }; diff --git a/src/components/vp-explorer/verb-selection.ts b/src/components/vp-explorer/verb-selection.ts index 89e2028..c2d8d81 100644 --- a/src/components/vp-explorer/verb-selection.ts +++ b/src/components/vp-explorer/verb-selection.ts @@ -3,7 +3,6 @@ import { } from "../np-picker/picker-tools"; import * as T from "../../types"; import { getVerbInfo } from "../../lib/verb-info"; -import { isPerfectTense } from "../../lib/phrase-building/vp-tools"; export function makeVPSelectionState( verb: T.VerbEntry, @@ -46,28 +45,15 @@ export function makeVPSelectionState( : "dynamic" in info ? { entry: info.dynamic.auxVerb } as T.VerbEntry : undefined; - const tenseSelection = ((): { tenseCategory: "perfect", tense: T.PerfectTense } | { - tenseCategory: "basic" | "modal", - tense: T.VerbTense, - } => { - if (!os) { - return { tense: "presentVerb", tenseCategory: "basic" }; - } - if (os.verb.tenseCategory === "modal") { - return { tenseCategory: "modal", tense: isPerfectTense(os.verb.tense) ? "presentVerb" : os.verb.tense }; - } - if (os.verb.tenseCategory === "basic") { - return { tenseCategory: "basic", tense: isPerfectTense(os.verb.tense) ? "presentVerb" : os.verb.tense }; - } - return { tenseCategory: "perfect", tense: isPerfectTense(os.verb.tense) ? os.verb.tense : "present perfect" }; - })(); return { subject, verb: { type: "verb", verb: verb, dynAuxVerb, - ...tenseSelection, + verbTense: os ? os.verb.verbTense : "presentVerb", + perfectTense: os ? os.verb.perfectTense : "presentPerfect", + tenseCategory: os ? os.verb.tenseCategory : "basic", object, transitivity, isCompound, diff --git a/src/lib/pashto-inflector.test.ts b/src/lib/pashto-inflector.test.ts index 289ae85..3c91a47 100644 --- a/src/lib/pashto-inflector.test.ts +++ b/src/lib/pashto-inflector.test.ts @@ -674,6 +674,22 @@ const nouns: Array<{ } } }, + // TODO: uncomment this + // { + // in: {"ts":1527812591,"i":6286,"p":"دواړه","f":"dwáaRu","g":"dwaaRu","e":"both","c":"n. m. pl. unisex / adj."}, + // out: { + // plural: { + // masc: [ + // [{ p: "دواړه", f: "dwáaRu" }], + // [{ p: "دواړو", f: "dwáaRo" }], + // ], + // fem: [ + // [{ p: "دواړې", f: "dwáaRe" }], + // [{ p: "دواړو", f: "dwáaRo" }], + // ], + // } + // } + // }, // Masculine non-inflecting { in: { diff --git a/src/lib/phrase-building/compile-vp.ts b/src/lib/phrase-building/compile-vp.ts index 94f90b3..b8e65a0 100644 --- a/src/lib/phrase-building/compile-vp.ts +++ b/src/lib/phrase-building/compile-vp.ts @@ -12,6 +12,7 @@ import { removeBa, removeDuplicates, } from "./vp-tools"; +import { isModalTense, isPerfectTense } from "../type-predicates"; type Form = T.FormVersion & { OSV?: boolean }; export function compileVP(VP: T.VPRendered, form: Form): { ps: T.SingleOrLengthOpts, e?: string [] }; @@ -129,7 +130,7 @@ function putKidsInKidsSection(segments: Segment[], kids: Segment[]): Segment[] { } function arrangeVerbWNegative(head: T.PsString | undefined, restRaw: T.PsString[], V: T.VerbRendered): Segment[][] { - const hasLeapfrog = V.tenseCategory === "modal" || V.tenseCategory === "perfect"; + const hasLeapfrog = isPerfectTense(V.tense) || isModalTense(V.tense); const rest = (() => { if (hasLeapfrog) { const [restF, restLast] = splitOffLeapfrogWord(restRaw); diff --git a/src/lib/phrase-building/english-vp-rendering.ts b/src/lib/phrase-building/english-vp-rendering.ts index c4f69d3..478d2a9 100644 --- a/src/lib/phrase-building/english-vp-rendering.ts +++ b/src/lib/phrase-building/english-vp-rendering.ts @@ -1,6 +1,11 @@ import * as T from "../../types"; import { getVerbBlockPosFromPerson, parseEc } from "../misc-helpers"; import * as grammarUnits from "../grammar-units"; +import { + isPerfectTense, + isVerbTense, + // isModalTense, +} from "../type-predicates"; function engHave(s: T.Person): string { function isThirdPersonSing(p: T.Person): boolean { @@ -15,7 +20,7 @@ function engHave(s: T.Person): string { export function renderEnglishVPBase({ subjectPerson, object, vs }: { subjectPerson: T.Person, object: T.NPSelection | T.ObjectNP, - vs: T.VerbSelection, + vs: T.VerbSelectionComplete, }): string[] { const ec = parseEc(vs.verb.entry.ec || ""); const ep = vs.verb.entry.ep; @@ -79,34 +84,34 @@ export function renderEnglishVPBase({ subjectPerson, object, vs }: { ]), }; const modalBuilders: Record< - T.VerbTense, + T.ModalTense, (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => string[] > = { - presentVerb: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + presentVerbModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ can${n ? "'t" : ""} ${isToBe(v) ? "be" : v[0]}`, ]), - subjunctiveVerb: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + subjunctiveVerbModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `that $SUBJ can${n ? "'t" : ""} ${isToBe(v) ? "be" : v[0]}`, ]), - imperfectiveFuture: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + imperfectiveFutureModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ will${n ? " not" : ""} be able to ${isToBe(v) ? "be" : v[0]}`, ]), - perfectiveFuture: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + perfectiveFutureModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ will${n ? " not" : ""} be able to ${isToBe(v) ? "be" : v[0]}`, ]), - imperfectivePast: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + imperfectivePastModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ ${engEquative("past", s)} ${n ? " not" : ""} able to ${isToBe(v) ? "be" : v[0]}`, `$SUBJ could${n ? " not" : ""} ${v[0]}`, ]), - perfectivePast: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + perfectivePastModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ ${engEquative("past", s)} ${n ? " not" : ""} able to ${isToBe(v) ? "be" : v[0]}`, `$SUBJ could${n ? " not" : ""} ${isToBe(v) ? "be" : v[0]}`, ]), - habitualImperfectivePast: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + habitualImperfectivePastModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ used to ${n ? " not" : ""} be able to ${isToBe(v) ? "be" : v[0]}`, `$SUBJ would ${n ? " not" : ""} be able to ${isToBe(v) ? "be" : v[0]}`, ]), - habitualPerfectivePast: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + habitualPerfectivePastModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ used to ${n ? " not" : ""} be able to ${isToBe(v) ? "be" : v[0]}`, `$SUBJ would ${n ? " not" : ""} be able to ${isToBe(v) ? "be" : v[0]}`, ]), @@ -115,25 +120,25 @@ export function renderEnglishVPBase({ subjectPerson, object, vs }: { T.PerfectTense, (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => string[] > = { - "present perfect": (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + presentPerfect: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ ${engHave(s)}${n ? " not" : ""} ${v[4]}`, ]), - "past perfect": (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + pastPerfect: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ had${n ? " not" : ""} ${v[4]}`, ]), - "habitual perfect": (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + habitualPerfect: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ ${engHave(s)}${n ? " not" : ""} ${v[4]}`, ]), - "subjunctive perfect": (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + subjunctivePerfect: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `that $SUBJ will have${n ? " not" : ""} ${v[4]}`, ]), - "future perfect": (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + futurePerfect: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ will${n ? " not" : ""} have ${v[4]}`, ]), - "wouldBe perfect": (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + wouldBePerfect: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ would${n ? " not" : ""} have ${v[4]}`, ]), - "pastSubjunctive perfect": (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + pastSubjunctivePerfect: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ would${n ? " not" : ""} have ${v[4]}`, `$SUBJ should${n ? " not" : ""} have ${v[4]}`, ]), @@ -173,65 +178,65 @@ export function renderEnglishVPBase({ subjectPerson, object, vs }: { T.PerfectTense, (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => string[] > = { - "present perfect": (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + presentPerfect: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ ${engHave(s)}${n ? " not" : ""} been ${v[4]}`, ]), - "past perfect": (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + pastPerfect: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ had${n ? " not" : ""} been ${v[4]}`, ]), - "habitual perfect": (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + habitualPerfect: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ ${engHave(s)}${n ? " not" : ""} been ${v[4]}`, ]), - "subjunctive perfect": (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + subjunctivePerfect: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `that $SUBJ will${n ? " not" : ""} have been ${v[4]}`, ]), - "future perfect": (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + futurePerfect: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ will${n ? " not" : ""} have been ${v[4]}`, ]), - "wouldBe perfect": (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + wouldBePerfect: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ will${n ? " not" : ""} have been ${v[4]}`, ]), - "pastSubjunctive perfect": (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + pastSubjunctivePerfect: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ would${n ? " not" : ""} have been ${v[4]}`, ]), } const passiveModalBuilders: Record< - T.VerbTense, + T.ModalTense, (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => string[] > = { - presentVerb: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + presentVerbModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ can${n ? " not" : ""} be ${v[4]}`, `$SUBJ ${engEquative("present", s)}${n ? " not" : ""} able to be ${v[4]}`, ]), - subjunctiveVerb: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + subjunctiveVerbModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `that $SUBJ will${n ? " not" : ""} be able to be ${v[4]}`, `that $SUBJ ${n ? " not" : ""} be able to be ${v[4]}`, ]), - imperfectiveFuture: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + imperfectiveFutureModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ will${n ? " not" : ""} be able to be ${v[4]}`, ]), - perfectiveFuture: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + perfectiveFutureModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ will${n ? " not" : ""} be able to be ${v[4]}`, ]), - imperfectivePast: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + imperfectivePastModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ would${n ? " not" : ""} be able to be ${v[4]}`, `$SUBJ ${engEquative("past", s)}${n ? " not" : ""} being able to be ${v[4]}`, ]), - perfectivePast: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + perfectivePastModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ ${engEquative("past", s)}${n ? " not" : ""} able to be ${v[4]}`, ]), - habitualPerfectivePast: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + habitualPerfectivePastModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ would${n ? " not" : ""} be able to be ${v[4]}`, ]), - habitualImperfectivePast: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ + habitualImperfectivePastModal: (s: T.Person, v: T.EnglishVerbConjugationEc, n: boolean) => ([ `$SUBJ would${n ? " not" : ""} be able to be ${v[4]}`, ]), }; const base = ( - (vs.tenseCategory === "perfect") - ? (vs.voice === "active" ? perfectBuilders : passivePerfectBuilders)[vs.tense] - : vs.tenseCategory === "basic" - ? (vs.voice === "active" ? basicBuilders : passiveBasicBuilders)[vs.tense] - : (vs.voice === "active" ? modalBuilders : passiveModalBuilders)[vs.tense])(subjectPerson, ec, vs.negative); + isPerfectTense(vs.tense) + ? (vs.voice === "active" ? perfectBuilders : passivePerfectBuilders)[vs.tense] + : isVerbTense(vs.tense) + ? (vs.voice === "active" ? basicBuilders : passiveBasicBuilders)[vs.tense] + : (vs.voice === "active" ? modalBuilders : passiveModalBuilders)[vs.tense])(subjectPerson, ec, vs.negative); return base.map(b => `${b}${typeof object === "object" ? " $OBJ" : ""}${ep ? ` ${ep}` : ""}`); } diff --git a/src/lib/phrase-building/render-vp.ts b/src/lib/phrase-building/render-vp.ts index ad91477..3e21f3b 100644 --- a/src/lib/phrase-building/render-vp.ts +++ b/src/lib/phrase-building/render-vp.ts @@ -19,10 +19,12 @@ import { getPersonFromNP, removeBa, isPastTense, - isPerfectTense, getTenseVerbForm, } from "./vp-tools"; -import { isPattern4Entry } from "../type-predicates"; +import { + isPattern4Entry, + isPerfectTense, +} from "../type-predicates"; import { renderEnglishVPBase } from "./english-vp-rendering"; // TODO: ISSUE GETTING SPLIT HEAD NOT MATCHING WITH FUTURE VERBS @@ -129,7 +131,7 @@ function renderParticipleSelection(p: T.ParticipleSelection, inflected: boolean) }; } -function renderVerbSelection(vs: T.VerbSelection, person: T.Person, objectPerson: T.Person | undefined): T.VerbRendered { +function renderVerbSelection(vs: T.VerbSelectionComplete, person: T.Person, objectPerson: T.Person | undefined): T.VerbRendered { const v = vs.dynAuxVerb || vs.verb; const conjugations = conjugateVerb(v.entry, v.complement); // TODO: error handle this? @@ -148,14 +150,14 @@ function renderVerbSelection(vs: T.VerbSelection, person: T.Person, objectPerson } } -function getPsVerbConjugation(conj: T.VerbConjugation, vs: T.VerbSelection, person: T.Person, objectPerson: T.Person | undefined): { +function getPsVerbConjugation(conj: T.VerbConjugation, vs: T.VerbSelectionComplete, person: T.Person, objectPerson: T.Person | undefined): { ps: { head: T.PsString | undefined, rest: T.SingleOrLengthOpts, }, hasBa: boolean, } { - const f = getTenseVerbForm(conj, vs.tense, vs.tenseCategory, vs.voice); + const f = getTenseVerbForm(conj, vs.tense, vs.voice); const block = getMatrixBlock(f, objectPerson, person); const perfective = isPerfective(vs.tense); const verbForm = getVerbFromBlock(block, person); @@ -324,7 +326,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): boolean { +function isPerfective(t: T.VerbTense | T.PerfectTense | T.ModalTense): boolean { if (isPerfectTense(t)) return false; if (t === "presentVerb" || t === "imperfectiveFuture" || t === "imperfectivePast" || t === "habitualImperfectivePast") { return false; @@ -332,6 +334,9 @@ function isPerfective(t: T.VerbTense | T.PerfectTense): boolean { if (t === "perfectiveFuture" || t === "subjunctiveVerb" || t === "perfectivePast" || t === "habitualPerfectivePast") { return true; } + if (t === "perfectiveFutureModal" || t === "subjunctiveVerbModal" || t === "perfectivePastModal" || t === "habitualPerfectivePastModal") { + return true; + } throw new Error("tense not implemented yet"); } diff --git a/src/lib/phrase-building/vp-tools.ts b/src/lib/phrase-building/vp-tools.ts index 245b937..a1200e8 100644 --- a/src/lib/phrase-building/vp-tools.ts +++ b/src/lib/phrase-building/vp-tools.ts @@ -4,6 +4,7 @@ import { psRemove, psStringEquals, } from "../../lib/p-text-helpers"; +import { isPerfectTense } from "../type-predicates"; import * as grammarUnits from "../../lib/grammar-units"; export function isInvalidSubjObjCombo(subj: T.Person, obj: T.Person): boolean { @@ -26,79 +27,75 @@ export function isInvalidSubjObjCombo(subj: T.Person, obj: T.Person): boolean { ); } -export function getTenseVerbForm(conjR: T.VerbConjugation, tense: T.VerbTense | T.PerfectTense, tenseCategory: "basic" | "modal" | "perfect", voice: "active" | "passive"): T.VerbForm { +export function getTenseVerbForm(conjR: T.VerbConjugation, tense: T.VerbTense | T.PerfectTense | T.ModalTense, voice: "active" | "passive"): T.VerbForm { const conj = (voice === "passive" && conjR.passive) ? conjR.passive : conjR; - if (tenseCategory === "basic") { - if (tense === "presentVerb") { - return conj.imperfective.nonImperative; - } - if (tense === "subjunctiveVerb") { - return conj.perfective.nonImperative; - } - if (tense === "imperfectiveFuture") { - return conj.imperfective.future; - } - if (tense === "perfectiveFuture") { - return conj.perfective.future; - } - if (tense === "imperfectivePast") { - return conj.imperfective.past; - } - if (tense === "perfectivePast") { - return conj.perfective.past; - } - if (tense === "habitualImperfectivePast") { - return conj.imperfective.habitualPast; - } - if (tense === "habitualPerfectivePast") { - return conj.perfective.habitualPast; - } + if (tense === "presentVerb") { + return conj.imperfective.nonImperative; } - if (tenseCategory === "modal") { - if (tense === "presentVerb") { - return conj.imperfective.modal.nonImperative; - } - if (tense === "subjunctiveVerb") { - return conj.perfective.modal.nonImperative; - } - if (tense === "imperfectiveFuture") { - return conj.imperfective.modal.future; - } - if (tense === "perfectiveFuture") { - return conj.perfective.modal.future; - } - if (tense === "imperfectivePast") { - return conj.imperfective.modal.past; - } - if (tense === "perfectivePast") { - return conj.perfective.modal.past; - } - if (tense === "habitualImperfectivePast") { - return conj.imperfective.modal.habitualPast; - } - if (tense === "habitualPerfectivePast") { - return conj.perfective.modal.habitualPast; - } + if (tense === "subjunctiveVerb") { + return conj.perfective.nonImperative; } - if (tense === "present perfect") { + if (tense === "imperfectiveFuture") { + return conj.imperfective.future; + } + if (tense === "perfectiveFuture") { + return conj.perfective.future; + } + if (tense === "imperfectivePast") { + return conj.imperfective.past; + } + if (tense === "perfectivePast") { + return conj.perfective.past; + } + if (tense === "habitualImperfectivePast") { + return conj.imperfective.habitualPast; + } + if (tense === "habitualPerfectivePast") { + return conj.perfective.habitualPast; + } + if (tense === "presentVerbModal") { + return conj.imperfective.modal.nonImperative; + } + if (tense === "subjunctiveVerbModal") { + return conj.perfective.modal.nonImperative; + } + if (tense === "imperfectiveFutureModal") { + return conj.imperfective.modal.future; + } + if (tense === "perfectiveFutureModal") { + return conj.perfective.modal.future; + } + if (tense === "imperfectivePastModal") { + return conj.imperfective.modal.past; + } + if (tense === "perfectivePastModal") { + return conj.perfective.modal.past; + } + if (tense === "habitualImperfectivePastModal") { + return conj.imperfective.modal.habitualPast; + } + if (tense === "habitualPerfectivePastModal") { + return conj.perfective.modal.habitualPast; + } + if (tense === "presentPerfect") { return conj.perfect.present; } - if (tense === "past perfect") { + if (tense === "pastPerfect") { return conj.perfect.past; } - if (tense === "future perfect") { + if (tense === "futurePerfect") { return conj.perfect.future; } - if (tense === "habitual perfect") { + if (tense === "habitualPerfect") { return conj.perfect.habitual; } - if (tense === "subjunctive perfect") { + if (tense === "subjunctivePerfect") { return conj.perfect.subjunctive; } - if (tense === "wouldBe perfect") { + if (tense === "wouldBePerfect") { return conj.perfect.affirmational; } - if (tense === "pastSubjunctive perfect") { + if (tense === "pastSubjunctivePerfect") { return conj.perfect.pastSubjunctiveHypothetical; } throw new Error("unknown tense"); @@ -126,23 +123,45 @@ export function removeBa(ps: T.PsString): T.PsString { return psRemove(ps, concatPsString(grammarUnits.baParticle, " ")); } -export function isEquativeTense(t: T.VerbTense | T.EquativeTense | T.PerfectTense): t is T.EquativeTense { - return (t === "present" || t === "future" || t === "habitual" || t === "past" || t === "wouldBe" || t === "subjunctive" || t === "pastSubjunctive"); +export function getTenseFromVerbSelection(vs: T.VerbSelection): T.VerbTense | T.PerfectTense | T.ModalTense { + function verbTenseToModalTense(tn: T.VerbTense): T.ModalTense { + if (tn === "presentVerb") { + return "presentVerbModal"; + } + if (tn === "subjunctiveVerb") { + return "subjunctiveVerbModal"; + } + if (tn === "imperfectiveFuture") { + return "imperfectiveFutureModal"; + } + if (tn === "perfectiveFuture") { + return "perfectiveFutureModal"; + } + if (tn === "perfectivePast") { + return "perfectiveFutureModal"; + } + if (tn === "imperfectivePast") { + return "imperfectivePastModal"; + } + if (tn === "habitualImperfectivePast") { + return "habitualImperfectivePastModal"; + } + if (tn === "habitualPerfectivePast") { + return "habitualPerfectivePastModal"; + } + throw new Error("can't convert non verbTense to modalTense"); + } + if (vs.tenseCategory === "basic") { + return vs.verbTense; + } + if (vs.tenseCategory === "perfect") { + return vs.perfectTense; + } + // vs.tenseCategory === "modal" + return verbTenseToModalTense(vs.verbTense); } -export function isPerfectTense(t: T.VerbTense | T.EquativeTense | T.PerfectTense): t is T.PerfectTense { - return ( - t === "present perfect" || - t === "habitual perfect" || - t === "future perfect" || - t === "past perfect" || - t === "wouldBe perfect" || - t === "subjunctive perfect" || - t === "pastSubjunctive perfect" - ); -} - -export function isPastTense(tense: T.VerbTense | T.PerfectTense): boolean { +export function isPastTense(tense: T.VerbTense | T.PerfectTense | T.ModalTense): boolean { if (isPerfectTense(tense)) return true; return tense.toLowerCase().includes("past"); } @@ -155,15 +174,52 @@ export function removeDuplicates(psv: T.PsString[]): T.PsString[] { )); } -export function switchSubjObj({ subject, verb }: T.VPSelection): T.VPSelection { - if (!subject|| !verb || !verb.object || !(typeof verb.object === "object")) { - return { subject, verb }; +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")) { + return vps; + } + return { + ...vps, + subject: vps.verb.object, + verb: { + ...vps.verb, + object: vps.subject, + }, + }; + } + if (!vps.subject|| !vps.verb || !(typeof vps.verb.object === "object")) { + return vps; } return { - subject: verb.object, + ...vps, + subject: vps.verb.object, verb: { - ...verb, - object: subject, + ...vps.verb, + object: vps.subject, } }; +} + +export function completeVPSelection(vps: T.VPSelection): T.VPSelectionComplete | undefined { + if (vps.subject === undefined) { + return undefined; + } + if (vps.verb.object === undefined) { + return undefined; + } + // necessary for this version on typscript ... + const verb: T.VerbSelectionComplete = { + ...vps.verb, + object: vps.verb.object, + tense: getTenseFromVerbSelection(vps.verb), + }; + const subject = vps.subject; + return { + ...vps, + subject, + verb, + }; } \ No newline at end of file diff --git a/src/lib/type-predicates.ts b/src/lib/type-predicates.ts index eaa6ab3..e9400a9 100644 --- a/src/lib/type-predicates.ts +++ b/src/lib/type-predicates.ts @@ -160,9 +160,30 @@ export function isArrayOneOrMore(a: U[]): a is T.ArrayOneOrMore { return a.length > 0; } -export function isVPSelectionComplete(vps: T.VPSelection | T.VPSelectionComplete): vps is T.VPSelectionComplete { - if ((vps.subject !== undefined) && (vps.verb.object !== undefined)) { - return true; - } - return false; +export function isPerfectTense(tense: T.VerbTense | T.EquativeTense | T.ModalTense | T.PerfectTense): tense is T.PerfectTense { + return tense.endsWith("Perfect"); } + +export function isVerbTense(tense: T.VerbTense | T.EquativeTense | T.ModalTense | T.PerfectTense): tense is T.VerbTense { + const verbTenses: T.VerbTense[] = [ + "presentVerb", + "subjunctiveVerb", + "perfectiveFuture", + "imperfectiveFuture", + "perfectivePast", + "imperfectivePast", + "habitualPerfectivePast", + "habitualImperfectivePast", + ]; + return verbTenses.some(x => x === tense); +} + +export function isModalTense(tense: T.VerbTense | T.EquativeTense | T.ModalTense | T.PerfectTense): tense is T.ModalTense { + return tense.endsWith("Modal"); +} + +export function isEquativeTense(t: T.VerbTense | T.EquativeTense | T.PerfectTense | T.ModalTense): t is T.EquativeTense { + return (t === "present" || t === "future" || t === "habitual" || t === "past" || t === "wouldBe" || t === "subjunctive" || t === "pastSubjunctive"); +} + + diff --git a/src/types.ts b/src/types.ts index 3e58cd6..737e161 100644 --- a/src/types.ts +++ b/src/types.ts @@ -535,7 +535,8 @@ export type VerbTense = "presentVerb" export type EquativeTense = "present" | "subjunctive" | "habitual" | "past" | "future" | "wouldBe" | "pastSubjunctive"; export type NounNumber = "singular" | "plural"; -export type PerfectTense = `${EquativeTense} perfect`; +export type PerfectTense = `${EquativeTense}Perfect`; +export type ModalTense = `${VerbTense}Modal`; export type VPSelection = { subject: NPSelection | undefined, @@ -547,9 +548,10 @@ export type VPSelectionComplete = { verb: VerbSelectionComplete, }; -export type VerbSelectionComplete = Exclude & { +export type VerbSelectionComplete = Omit & { object: Exclude, -}; + tense: VerbTense | PerfectTense | ModalTense, +} export type VerbSelection = { type: "verb", @@ -565,15 +567,12 @@ export type VerbSelection = { // TODO: changeStativeDynamic // TODO: add in aspect element here?? negative: boolean, -} & ({ - tense: VerbTense, - tenseCategory: "basic" | "modal", -} | { - tense: PerfectTense, - tenseCategory: "perfect" -}); + verbTense: VerbTense, + perfectTense: PerfectTense, + tenseCategory: "basic" | "modal" | "perfect", +}; -export type VerbRendered = Omit & { +export type VerbRendered = Omit & { ps: { head: PsString | undefined, rest: SingleOrLengthOpts<