APs and moveable blocks for verb phrases!

This commit is contained in:
lingdocs 2022-05-25 18:47:19 -05:00
parent 46fcc5cbb5
commit 9f82e6d085
22 changed files with 633 additions and 372 deletions

View File

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

@ -195,11 +195,10 @@ function ConjugationViewer({ entry, complement, textOptions, showOnly, highlight
try { try {
return conjugateVerb(entry, complement); return conjugateVerb(entry, complement);
} catch(e) { } catch(e) {
console.log("conjugation error", e); console.error("conjugation error", e);
return undefined; return undefined;
} }
})(); })();
console.log(conjugation);
if (conjugation === undefined) { if (conjugation === undefined) {
// don't show the conjugation viewer if the verb can't be conjugated // don't show the conjugation viewer if the verb can't be conjugated
return null; return null;

View File

@ -40,7 +40,7 @@ function EPExplorer(props: {
const parent = useRef<HTMLDivElement>(null); const parent = useRef<HTMLDivElement>(null);
useEffect(() => { useEffect(() => {
parent.current && autoAnimate(parent.current); parent.current && autoAnimate(parent.current);
}, [parent]) }, [parent]);
const subject = getSubjectSelection(eps.blocks).selection; const subject = getSubjectSelection(eps.blocks).selection;
const king = subject?.type === "pronoun" const king = subject?.type === "pronoun"
? "subject" ? "subject"
@ -65,7 +65,7 @@ function EPExplorer(props: {
handleChange={setMode} handleChange={setMode}
/> />
</div> </div>
<div className="clickable h5" onClick={() => adjustEps({ type: "insert new AP" })}>+ AP</div> {mode === "phrases" && <div className="clickable h5" onClick={() => adjustEps({ type: "insert new AP" })}>+ AP</div>}
<div ref={parent} className="d-flex flex-row justify-content-around flex-wrap" style={{ marginLeft: "-0.5rem", marginRight: "-0.5rem" }}> <div ref={parent} className="d-flex flex-row justify-content-around flex-wrap" style={{ marginLeft: "-0.5rem", marginRight: "-0.5rem" }}>
{mode === "phrases" && <> {mode === "phrases" && <>
{eps.blocks.map(({ block, key }, i) => ( {eps.blocks.map(({ block, key }, i) => (

View File

@ -5,8 +5,7 @@ import {
} from "../../lib/misc-helpers"; } from "../../lib/misc-helpers";
import { isUnisexNounEntry } from "../../lib/type-predicates"; import { isUnisexNounEntry } from "../../lib/type-predicates";
import { checkForMiniPronounsError } from "../../lib/phrase-building/compile"; import { checkForMiniPronounsError } from "../../lib/phrase-building/compile";
import { adjustSubjectSelection, getSubjectSelection, makeAPBlock } from "../../lib/phrase-building/blocks-utils"; import { adjustSubjectSelection, getSubjectSelection, insertNewAP, removeAP, setAP, shiftBlock } from "../../lib/phrase-building/blocks-utils";
import { assertNever } from "assert-never";
type EpsReducerAction = { type EpsReducerAction = {
type: "set predicate type", type: "set predicate type",
@ -151,50 +150,30 @@ export default function epsReducer(eps: T.EPSelectionState, action: EpsReducerAc
if (action.type === "insert new AP") { if (action.type === "insert new AP") {
return { return {
...eps, ...eps,
blocks: [makeAPBlock(), ...eps.blocks], blocks: insertNewAP(eps.blocks),
}; };
// const index = action.payload;
// const newAP = undefined;
// return {
// ...eps,
// blocks: [...eps.blocks].splice(index, 0, newAP),
// };
} }
if (action.type === "set AP") { if (action.type === "set AP") {
const blocks = [...eps.blocks];
const { index, AP } = action.payload; const { index, AP } = action.payload;
blocks[index].block = AP;
return { return {
...eps, ...eps,
blocks, blocks: setAP(eps.blocks, index, AP),
}; };
} }
if (action.type === "remove AP") { if (action.type === "remove AP") {
const blocks = [...eps.blocks];
blocks.splice(action.payload, 1);
return { return {
...eps, ...eps,
blocks, blocks: removeAP(eps.blocks, action.payload),
}; };
} }
if (action.type === "shift block") { if (action.type === "shift block") {
const { index, direction } = action.payload; const { index, direction } = action.payload;
const newIndex = index + (direction === "forward" ? 1 : -1);
const blocks = [...eps.blocks];
if (newIndex >= blocks.length || newIndex < 0) {
return eps;
// var k = newIndex - blocks.length + 1;
// while (k--) {
// blocks.push(undefined);
// }
}
blocks.splice(newIndex, 0, blocks.splice(index, 1)[0]);
return { return {
...eps, ...eps,
blocks, blocks: shiftBlock(eps.blocks, index, direction),
}; };
} }
assertNever(action); throw new Error("unknown epsReducer action");
} }
function ensureMiniPronounsOk(old: T.EPSelectionState, eps: T.EPSelectionState, sendAlert?: (msg: string) => void): T.EPSelectionState { function ensureMiniPronounsOk(old: T.EPSelectionState, eps: T.EPSelectionState, sendAlert?: (msg: string) => void): T.EPSelectionState {

View File

@ -58,7 +58,7 @@ function AdjectiveManager(props: {
<div>Adjective</div> <div>Adjective</div>
<div className="d-flex flex-row align-items-baseline"> <div className="d-flex flex-row align-items-baseline">
{!!props.adjectives.length && !adding && <div> {!!props.adjectives.length && !adding && <div>
<h6 onClick={() => setAdding(true)}>+ Adj.</h6> <h6 className="clickable" onClick={() => setAdding(true)}>+ Adj.</h6>
</div>} </div>}
<div onClick={deleteAdj(i)} className="ml-4"> <div onClick={deleteAdj(i)} className="ml-4">
<div className="fas fa-trash" /> <div className="fas fa-trash" />
@ -76,7 +76,7 @@ function AdjectiveManager(props: {
/> />
</div>)} </div>)}
{!adding && !props.adjectives.length && <h6 className="clickable" style={{ float: "right" }}> {!adding && !props.adjectives.length && <h6 className="clickable" style={{ float: "right" }}>
<div onClick={() => setAdding(true)}>+ Adj.</div> <div className="clickable" onClick={() => setAdding(true)}>+ Adj.</div>
</h6>} </h6>}
</div>; </div>;
} }

View File

@ -46,9 +46,11 @@ function AdjectivePicker(props: {
{!props.noTitle && <div> {!props.noTitle && <div>
<h6>Adjective</h6> <h6>Adjective</h6>
</div>} </div>}
{(!addingSandwich && props.adjective && !props.adjective?.sandwich) {/* not ready for sandwiches on adjectives */}
{/* {(!addingSandwich && props.adjective && !props.adjective?.sandwich)
? <div className="clickable" onClick={() => setAddingSandwich(true)}>+ Sandwich</div> ? <div className="clickable" onClick={() => setAddingSandwich(true)}>+ Sandwich</div>
: <div></div>} : <div></div>} */}
<div />
</div> </div>
<div className="mt-1"> <div className="mt-1">
<EntrySelect <EntrySelect

View File

@ -24,7 +24,13 @@ import {
} from "../../lib/translate-phonetics"; } from "../../lib/translate-phonetics";
export function makeVerbSelectOption(e: T.VerbEntry, opts: T.TextOptions): { value: string, label: string | JSX.Element } { export function makeVerbSelectOption(e: T.VerbEntry, opts: T.TextOptions): { value: string, label: string | JSX.Element } {
const engV = getEnglishVerb(e.entry); const engV = (() => {
try {
return getEnglishVerb(e.entry);
} catch(e) {
console.error("no english conjugations for verb");
}
})();
const eng = engV || truncateEnglish(e.entry.e); const eng = engV || truncateEnglish(e.entry.e);
const ps = plainTextPsAdjustment( const ps = plainTextPsAdjustment(
{ p: e.entry.p, f: removeFVarients(e.entry.f) }, { p: e.entry.p, f: removeFVarients(e.entry.f) },

View File

@ -1,37 +1,28 @@
import { compileVP } from "../../lib/phrase-building/compile"; import { compileVP } from "../../lib/phrase-building/compile";
import * as T from "../../types"; import * as T from "../../types";
import AbbreviationFormSelector from "./AbbreviationFormSelector"; import AbbreviationFormSelector from "./AbbreviationFormSelector";
import useStickyState from "../../lib/useStickyState";
import Examples from "../Examples"; import Examples from "../Examples";
import { getObjectSelection, getSubjectSelection } from "../../lib/phrase-building/blocks-utils";
function VPDisplay({ VP, opts, setForm }: { function VPDisplay({ VP, opts, setForm }: {
VP: T.VPSelectionState | T.VPRendered, VP: T.VPSelectionState | T.VPRendered,
opts: T.TextOptions, opts: T.TextOptions,
setForm: (form: T.FormVersion) => void, setForm: (form: T.FormVersion) => void,
}) { }) {
const [OSV, setOSV] = useStickyState<boolean>(false, "includeOSV");
if (!("type" in VP)) { if (!("type" in VP)) {
return <div className="lead text-muted text-center mt-4"> return <div className="lead text-muted text-center mt-4">
{(() => { {(() => {
const twoNPs = (VP.subject === undefined) && (VP.object === undefined); const subject = getSubjectSelection(VP.blocks).selection;
return `Choose NP${twoNPs ? "s " : ""} to make a phrase`; const object = getObjectSelection(VP.blocks).selection;
if (subject === undefined || object || undefined) {
return `Choose NP${((subject === undefined) && (object === undefined)) ? "s " : ""} to make a phrase`;
}
return `Choose/remove AP to complete the phrase`;
})()} })()}
</div>; </div>;
} }
const result = compileVP(VP, { ...VP.form, OSV }); const result = compileVP(VP, { ...VP.form });
return <div className="text-center mt-1"> return <div className="text-center mt-1">
{VP.verb.transitivity === "transitive" && <div className="form-check mb-2">
<input
className="form-check-input"
type="checkbox"
checked={OSV}
id="OSVCheckbox"
onChange={e => setOSV(e.target.checked)}
/>
<label className="form-check-label text-muted" htmlFor="OSVCheckbox">
Include O S V
</label>
</div>}
<AbbreviationFormSelector <AbbreviationFormSelector
adjustable={VP.whatsAdjustable} adjustable={VP.whatsAdjustable}
form={VP.form} form={VP.form}

View File

@ -7,7 +7,7 @@ import * as T from "../../types";
import ChartDisplay from "./VPChartDisplay"; import ChartDisplay from "./VPChartDisplay";
import useStickyState, { useStickyReducer } from "../../lib/useStickyState"; import useStickyState, { useStickyReducer } from "../../lib/useStickyState";
import { makeVPSelectionState } from "./verb-selection"; import { makeVPSelectionState } from "./verb-selection";
import { useEffect, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { getKingAndServant, renderVP } from "../../lib/phrase-building/render-vp"; import { getKingAndServant, renderVP } from "../../lib/phrase-building/render-vp";
import { completeVPSelection, isPastTense } from "../../lib/phrase-building/vp-tools"; import { completeVPSelection, isPastTense } from "../../lib/phrase-building/vp-tools";
import VPExplorerQuiz from "./VPExplorerQuiz"; import VPExplorerQuiz from "./VPExplorerQuiz";
@ -16,6 +16,9 @@ import VPExplorerExplanationModal, { roleIcon } from "./VPExplorerExplanationMod
import LZString from "lz-string"; import LZString from "lz-string";
import { vpsReducer } from "./vps-reducer"; import { vpsReducer } from "./vps-reducer";
import { getShrunkenServant } from "../../lib/phrase-building/compile"; import { getShrunkenServant } from "../../lib/phrase-building/compile";
import APPicker from "../ap-picker/APPicker";
import autoAnimate from "@formkit/auto-animate";
import { getObjectSelection, getSubjectSelection, isNoObject } from "../../lib/phrase-building/blocks-utils";
const phraseURLParam = "VPPhrase"; const phraseURLParam = "VPPhrase";
@ -41,7 +44,7 @@ function VPExplorer(props: {
props.loaded props.loaded
? props.loaded ? props.loaded
: savedVps => makeVPSelectionState(props.verb, savedVps), : savedVps => makeVPSelectionState(props.verb, savedVps),
"vpsState9", "vpsState12",
flashMessage, flashMessage,
); );
const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">( const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">(
@ -55,6 +58,10 @@ function VPExplorer(props: {
const [showShareClipped, setShowShareClipped] = useState<boolean>(false); const [showShareClipped, setShowShareClipped] = useState<boolean>(false);
const [alert, setAlert] = useState<string | undefined>(undefined); const [alert, setAlert] = useState<string | undefined>(undefined);
const [showingExplanation, setShowingExplanation] = useState<{ role: "servant" | "king", item: "subject" | "object" } | false>(false); const [showingExplanation, setShowingExplanation] = useState<{ role: "servant" | "king", item: "subject" | "object" } | false>(false);
const parent = useRef<HTMLDivElement>(null);
useEffect(() => {
parent.current && autoAnimate(parent.current);
}, [parent]);
const isPast = isPastTense(vps.verb.tenseCategory === "perfect" ? vps.verb.perfectTense : vps.verb.verbTense); const isPast = isPastTense(vps.verb.tenseCategory === "perfect" ? vps.verb.perfectTense : vps.verb.verbTense);
const roles = getKingAndServant( const roles = getKingAndServant(
isPast, isPast,
@ -75,7 +82,6 @@ function VPExplorer(props: {
}, [props.verb]); }, [props.verb]);
useEffect(() => { useEffect(() => {
const VPSFromUrl = getVPSFromUrl(); const VPSFromUrl = getVPSFromUrl();
console.log({ VPSFromUrl });
if (VPSFromUrl) { if (VPSFromUrl) {
setMode("phrases"); setMode("phrases");
adjustVps({ adjustVps({
@ -124,6 +130,8 @@ function VPExplorer(props: {
setShowShareClipped(false); setShowShareClipped(false);
}, 1250); }, 1250);
} }
const object = getObjectSelection(vps.blocks).selection;
const subject = getSubjectSelection(vps.blocks).selection;
const VPS = completeVPSelection(vps); const VPS = completeVPSelection(vps);
const phraseIsComplete = !!VPS; const phraseIsComplete = !!VPS;
const rendered = VPS ? renderVP(VPS) : undefined; const rendered = VPS ? renderVP(VPS) : undefined;
@ -160,16 +168,50 @@ function VPExplorer(props: {
{mode === "phrases" ? <i className="fas fa-share-alt" /> : ""} {mode === "phrases" ? <i className="fas fa-share-alt" /> : ""}
</div> </div>
</div> </div>
{(vps.verb && (typeof vps.object === "object") && (vps.verb.isCompound !== "dynamic") && (vps.verb.tenseCategory !== "imperative") &&(mode === "phrases")) && {(vps.verb && (typeof object === "object") && (vps.verb.isCompound !== "dynamic") && (vps.verb.tenseCategory !== "imperative") &&(mode === "phrases")) &&
<div className="text-center my-2"> <div className="text-center my-2">
<button onClick={handleSubjObjSwap} className="btn btn-sm btn-light"> <button onClick={handleSubjObjSwap} className="btn btn-sm btn-light">
<i className="fas fa-exchange-alt mr-2" /> subj/obj <i className="fas fa-exchange-alt mr-2" /> subj/obj
</button> </button>
</div>} </div>}
{mode !== "quiz" && <div className="d-flex flex-row justify-content-around flex-wrap" style={{ marginLeft: "-0.5rem", marginRight: "-0.5rem" }}> {mode === "phrases" && <div className="clickable h5" onClick={() => adjustVps({ type: "insert new AP" })}>+ AP</div>}
{mode !== "quiz" && <div ref={parent} className="d-flex flex-row justify-content-around flex-wrap" style={{ marginLeft: "-0.5rem", marginRight: "-0.5rem" }}>
{mode === "phrases" && <> {mode === "phrases" && <>
<div className="my-2" style={{ background: (servantIsShrunk && roles.servant === "subject") ? shrunkenBackground : "inherit" }}> {vps.blocks.map(({ block, key }, i, blocks) => {
<NPPicker if (isNoObject(block)) return null;
return <div className="my-2 card block-card p-1 mx-1" key={key} style={{
background: (servantIsShrunk && (
(roles.servant === "subject" && block?.type === "subjectSelection")
||
(roles.servant === "object" && block?.type === "objectSelection")
)) ? shrunkenBackground : "inherit",
}}>
<div className="d-flex flex-row justify-content-between mb-1" style={{ height: "1rem" }}>
{(i > 0 && !isNoObject(blocks[i - 1].block)) ? <div
className="small clickable ml-1"
onClick={() => adjustVps({ type: "shift block", payload: { index: i, direction: "back" }})}
>
<i className="fas fa-chevron-left" />
</div> : <div/>}
{(i < vps.blocks.length - 1 && !isNoObject(blocks[i + 1].block)) ? <div
className="small clickable mr-1"
onClick={() => adjustVps({ type: "shift block", payload: { index: i, direction: "forward" }})}
>
<i className="fas fa-chevron-right" />
</div> : <div/>}
</div>
{(!block || block.type === "adverb" || block.type === "sandwich")
? <APPicker
phraseIsComplete={phraseIsComplete}
heading="AP"
entryFeeder={props.entryFeeder}
AP={block}
opts={props.opts}
onChange={AP => adjustVps({ type: "set AP", payload: { index: i, AP } })}
onRemove={() => adjustVps({ type: "remove AP", payload: i })}
/>
: (block?.type === "subjectSelection")
? <NPPicker
phraseIsComplete={phraseIsComplete} phraseIsComplete={phraseIsComplete}
heading={roles.king === "subject" heading={roles.king === "subject"
? <div className="h5 text-center" onClick={() => setShowingExplanation({ role: "king", item: "subject" })}>Subject {roleIcon.king}</div> ? <div className="h5 text-center" onClick={() => setShowingExplanation({ role: "king", item: "subject" })}>Subject {roleIcon.king}</div>
@ -185,20 +227,19 @@ function VPExplorer(props: {
} }
</div>} </div>}
entryFeeder={props.entryFeeder} entryFeeder={props.entryFeeder}
np={block.selection}
counterPart={vps.verb ? object : undefined}
role={(isPast && vps.verb.transitivity !== "intransitive") role={(isPast && vps.verb.transitivity !== "intransitive")
? "ergative" ? "ergative"
: "subject" : "subject"
} }
is2ndPersonPicker={vps.verb.tenseCategory === "imperative"}
np={vps.subject}
counterPart={vps.verb ? vps.object : undefined}
onChange={handleSubjectChange} onChange={handleSubjectChange}
opts={props.opts} opts={props.opts}
isShrunk={(servantIsShrunk && roles.servant === "subject")} isShrunk={(servantIsShrunk && roles.servant === "subject")}
/> />
</div> : (vps.verb && block?.type === "objectSelection" && block.selection !== "none")
{vps.verb && (vps.object !== "none") && <div className="my-2" style={{ background: (servantIsShrunk && roles.servant === "object") ? shrunkenBackground : "inherit" }}> ? <div className="my-2" style={{ background: (servantIsShrunk && roles.servant === "object") ? shrunkenBackground : "inherit" }}>
{(typeof vps.object === "number") {(typeof block.selection === "number")
? <div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div> ? <div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div>
: <NPPicker : <NPPicker
phraseIsComplete={phraseIsComplete} phraseIsComplete={phraseIsComplete}
@ -217,13 +258,16 @@ function VPExplorer(props: {
</div>} </div>}
entryFeeder={props.entryFeeder} entryFeeder={props.entryFeeder}
role="object" role="object"
np={vps.object} np={block.selection}
counterPart={vps.subject} counterPart={subject}
onChange={handleObjectChange} onChange={handleObjectChange}
opts={props.opts} opts={props.opts}
isShrunk={(servantIsShrunk && roles.servant === "object")} isShrunk={(servantIsShrunk && roles.servant === "object")}
/>} />}
</div>} </div>
: null}
</div>;
})}
</>} </>}
<div className="my-2"> <div className="my-2">
<TensePicker <TensePicker

View File

@ -18,6 +18,7 @@ import energyDrink from "./energy-drink.jpg";
import { flattenLengths } from "../../lib/phrase-building/segment"; import { flattenLengths } from "../../lib/phrase-building/segment";
import { concatPsString } from "../../lib/p-text-helpers"; import { concatPsString } from "../../lib/p-text-helpers";
import { isImperativeTense } from "../../lib/type-predicates"; import { isImperativeTense } from "../../lib/type-predicates";
import { adjustObjectSelection, adjustSubjectSelection, getObjectSelection, getRenderedObjectSelection, getRenderedSubjectSelection, getSubjectSelection } from "../../lib/phrase-building/blocks-utils";
const correctEmoji = ["✅", '🤓', "✅", '😊', "🌹", "✅", "✅", '🥳', "👏", "✅", "💯", "😎", "✅", "👍"]; const correctEmoji = ["✅", '🤓', "✅", '😊', "🌹", "✅", "✅", '🥳', "👏", "✅", "💯", "😎", "✅", "👍"];
@ -97,7 +98,8 @@ function VPExplorerQuiz(props: {
} }
} }
const rendered = renderVP(quizState.vps); const rendered = renderVP(quizState.vps);
const { subject, object } = rendered; const subject = getRenderedSubjectSelection(rendered.blocks).selection;
const object = getRenderedObjectSelection(rendered.blocks).selection;
const { e } = compileVP(rendered, { removeKing: false, shrinkServant: false }); const { e } = compileVP(rendered, { removeKing: false, shrinkServant: false });
function handleRestart() { function handleRestart() {
setWithBa(false); setWithBa(false);
@ -177,9 +179,10 @@ function VPExplorerQuiz(props: {
type="checkbox" type="checkbox"
checked={withBa} checked={withBa}
onChange={e => setWithBa(e.target.checked)} onChange={e => setWithBa(e.target.checked)}
id="addBa"
/> />
<label className="form-check-label text-muted" htmlFor="OSVCheckbox"> <label className="form-check-label text-muted" htmlFor="addBa">
add <InlinePs opts={props.opts}>{baParticle}</InlinePs> in phrase add <InlinePs opts={props.opts}>{baParticle}</InlinePs> in kids' section
</label> </label>
</div> </div>
<button type="submit" className="btn btn-primary"> <button type="submit" className="btn btn-primary">
@ -372,11 +375,13 @@ function getOptionFromResult(r: {
} }
function completeVPs(vps: T.VPSelectionState): T.VPSelectionComplete { function completeVPs(vps: T.VPSelectionState): T.VPSelectionComplete {
const oldSubj = vps.subject?.type === "pronoun" const vpsSubj = getSubjectSelection(vps.blocks).selection;
? vps.subject.person const vpsObj = getObjectSelection(vps.blocks).selection;
const oldSubj = vpsSubj?.type === "pronoun"
? vpsSubj.person
: undefined; : undefined;
const oldObj = (typeof vps.object === "object" && vps.object.type === "pronoun") const oldObj = (typeof vpsObj === "object" && vpsObj.type === "pronoun")
? vps.object.person ? vpsObj.person
: undefined; : undefined;
const { subj, obj } = randomSubjObj( const { subj, obj } = randomSubjObj(
oldSubj === undefined oldSubj === undefined
@ -393,29 +398,34 @@ function completeVPs(vps: T.VPSelectionState): T.VPSelectionComplete {
}; };
return { return {
...vps, ...vps,
subject: { blocks: adjustObjectSelection(
adjustSubjectSelection(vps.blocks, {
type: "pronoun", type: "pronoun",
distance: "far", distance: "far",
person: subj, person: subj,
}, }),
object: ( (
(typeof vps.object === "object" && !(vps.object.type === "noun" && vps.object.dynamicComplement)) (typeof vpsObj === "object" && !(vpsObj.type === "noun" && vpsObj.dynamicComplement))
|| ||
vps.object === undefined vpsObj === undefined
) )
? { ? {
type: "pronoun", type: "pronoun",
distance: "far", distance: "far",
person: obj, person: obj,
} }
: vps.object, : vpsObj,
),
verb, verb,
}; };
} }
function getRandomVPSelection(mix: MixType = "both") { function getRandomVPSelection(mix: MixType = "both") {
// TODO: Type safety to make sure it's safe? // TODO: Type safety to make sure it's safe?
return ({ subject, verb, object }: T.VPSelectionComplete): T.VPSelectionComplete => { return (VPS: T.VPSelectionComplete): T.VPSelectionComplete => {
const subject = getSubjectSelection(VPS.blocks).selection;
const object = getObjectSelection(VPS.blocks).selection;
const verb = VPS.verb;
const oldSubj = (subject.type === "pronoun") const oldSubj = (subject.type === "pronoun")
? subject.person ? subject.person
: undefined; : undefined;
@ -444,27 +454,39 @@ function getRandomVPSelection(mix: MixType = "both") {
// ensure that the verb selection is complete // ensure that the verb selection is complete
if (mix === "tenses") { if (mix === "tenses") {
return { return {
subject: subject !== undefined ? subject : randSubj, blocks: possibleShuffleArray(adjustObjectSelection(
object: object !== undefined ? object : randObj, adjustSubjectSelection(VPS.blocks, subject !== undefined ? subject : randSubj),
object !== undefined ? object : randObj,
)),
verb: randomizeTense(verb, true), verb: randomizeTense(verb, true),
form: { removeKing: false, shrinkServant: false }, form: { removeKing: false, shrinkServant: false },
} }
} }
return { return {
subject: randSubj, blocks: possibleShuffleArray(adjustObjectSelection(
object: ( adjustSubjectSelection(VPS.blocks, randSubj),
(
(typeof object === "object" && !(object.type === "noun" && object.dynamicComplement)) (typeof object === "object" && !(object.type === "noun" && object.dynamicComplement))
|| ||
object === undefined object === undefined
) )
? randObj ? randObj
: object, : object,
)),
verb: randomizeTense(verb, true), verb: randomizeTense(verb, true),
form: { removeKing: false, shrinkServant: false }, form: { removeKing: false, shrinkServant: false },
}; };
}; };
}; };
function possibleShuffleArray<X>(arr: X[]): X[] {
const willShuffle = randFromArray([true, false, false]);
if (willShuffle) {
return shuffleArray(arr);
}
return arr;
}
function randomizeTense(verb: T.VerbSelectionComplete, dontRepeatTense: boolean): T.VerbSelectionComplete { function randomizeTense(verb: T.VerbSelectionComplete, dontRepeatTense: boolean): T.VerbSelectionComplete {
return { return {
...verb, ...verb,

View File

@ -3,6 +3,7 @@ import {
} from "../np-picker/picker-tools"; } from "../np-picker/picker-tools";
import * as T from "../../types"; import * as T from "../../types";
import { getVerbInfo } from "../../lib/verb-info"; import { getVerbInfo } from "../../lib/verb-info";
import { adjustObjectSelection, getObjectSelection, getSubjectSelection, makeObjectSelection, makeSubjectSelection } from "../../lib/phrase-building/blocks-utils";
export function makeVPSelectionState( export function makeVPSelectionState(
verb: T.VerbEntry, verb: T.VerbEntry,
@ -11,16 +12,17 @@ export function makeVPSelectionState(
const info = getVerbInfo(verb.entry, verb.complement); const info = getVerbInfo(verb.entry, verb.complement);
const subject = (os?.verb.voice === "passive" && info.type === "dynamic compound") const subject = (os?.verb.voice === "passive" && info.type === "dynamic compound")
? makeNounSelection(info.objComplement.entry as T.NounEntry, undefined, true) ? makeNounSelection(info.objComplement.entry as T.NounEntry, undefined, true)
: (os?.subject || undefined); : (os?.blocks ? getSubjectSelection(os.blocks) : undefined);
function getTransObjFromos() { function getTransObjFromos() {
const osObj = os ? getObjectSelection(os.blocks).selection : undefined;
if ( if (
!os || !os ||
os.object === "none" || osObj === "none" ||
typeof os.object === "number" || typeof osObj === "number" ||
os.verb.isCompound === "dynamic" || os.verb.isCompound === "dynamic" ||
(os.object?.type === "noun" && os.object.dynamicComplement) (osObj?.type === "noun" && osObj.dynamicComplement)
) return undefined; ) return undefined;
return os.object; return osObj;
} }
const transitivity: T.Transitivity = "grammaticallyTransitive" in info const transitivity: T.Transitivity = "grammaticallyTransitive" in info
? "transitive" ? "transitive"
@ -45,9 +47,12 @@ export function makeVPSelectionState(
: "dynamic" in info : "dynamic" in info
? { entry: info.dynamic.auxVerb } as T.VerbEntry ? { entry: info.dynamic.auxVerb } as T.VerbEntry
: undefined; : undefined;
const blocks = [
{ key: Math.random(), block: makeSubjectSelection(subject) },
{ key: Math.random(), block: makeObjectSelection(object) },
];
return { return {
subject, blocks,
object,
verb: { verb: {
type: "verb", type: "verb",
verb: verb, verb: verb,
@ -77,9 +82,12 @@ export function changeStatDyn(v: T.VPSelectionState, s: "dynamic" | "stative"):
} }
return { return {
...v, ...v,
object: s === "dynamic" blocks: adjustObjectSelection(
v.blocks,
s === "dynamic"
? makeNounSelection(info.dynamic.objComplement.entry as T.NounEntry, undefined, true) ? makeNounSelection(info.dynamic.objComplement.entry as T.NounEntry, undefined, true)
: undefined, : undefined,
),
verb: { verb: {
...v.verb, ...v.verb,
isCompound: s, isCompound: s,
@ -93,7 +101,7 @@ export function changeStatDyn(v: T.VPSelectionState, s: "dynamic" | "stative"):
export function changeTransitivity(v: T.VPSelectionState, transitivity: "transitive" | "grammatically transitive"): T.VPSelectionState { export function changeTransitivity(v: T.VPSelectionState, transitivity: "transitive" | "grammatically transitive"): T.VPSelectionState {
return { return {
...v, ...v,
object: transitivity === "grammatically transitive" ? T.Person.ThirdPlurMale : undefined, blocks: adjustObjectSelection(v.blocks, transitivity === "grammatically transitive" ? T.Person.ThirdPlurMale : undefined),
verb: { verb: {
...v.verb, ...v.verb,
transitivity, transitivity,

View File

@ -13,6 +13,7 @@ import { checkForMiniPronounsError } from "../../lib/phrase-building/compile";
import { import {
makeVPSelectionState, makeVPSelectionState,
} from "./verb-selection"; } from "./verb-selection";
import { adjustObjectSelection, adjustSubjectSelection, getObjectSelection, getSubjectSelection, insertNewAP, removeAP, setAP, shiftBlock } from "../../lib/phrase-building/blocks-utils";
export type VpsReducerAction = { export type VpsReducerAction = {
type: "load vps", type: "load vps",
@ -54,7 +55,24 @@ export type VpsReducerAction = {
} | { } | {
type: "set verb", type: "set verb",
payload: T.VerbEntry, payload: T.VerbEntry,
} } | {
type: "insert new AP",
} | {
type: "set AP",
payload: {
index: number,
AP: T.APSelection | undefined,
},
} | {
type: "remove AP",
payload: number,
} | {
type: "shift block",
payload: {
index: number,
direction: "back" | "forward",
},
};
export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, sendAlert?: (msg: string) => void): T.VPSelectionState { export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, sendAlert?: (msg: string) => void): T.VPSelectionState {
return ensureMiniPronounsOk(vps, doReduce()); return ensureMiniPronounsOk(vps, doReduce());
@ -68,38 +86,40 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
} }
function doReduce(): T.VPSelectionState { function doReduce(): T.VPSelectionState {
if (action.type === "load vps") { if (action.type === "load vps") {
console.log("doing loading", action.payload);
return action.payload; return action.payload;
} }
if (action.type === "set subject") { if (action.type === "set subject") {
const { subject, skipPronounConflictCheck } = action.payload; const { subject, skipPronounConflictCheck } = action.payload;
const object = getObjectSelection(vps.blocks).selection;
if ( if (
!skipPronounConflictCheck !skipPronounConflictCheck
&& &&
hasPronounConflict(subject, vps.object) hasPronounConflict(subject, object)
) { ) {
if (sendAlert) sendAlert("That combination of pronouns is not allowed"); if (sendAlert) sendAlert("That combination of pronouns is not allowed");
return vps; return vps;
} }
return { return {
...vps, ...vps,
subject: action.payload.subject, blocks: adjustSubjectSelection(vps.blocks, action.payload.subject),
}; };
} }
if (action.type === "set object") { if (action.type === "set object") {
if (!vps.verb) return vps; if (!vps.verb) return vps;
if ((vps.object === "none") || (typeof vps.object === "number")) { const objectB = getObjectSelection(vps.blocks).selection;
const subjectB = getSubjectSelection(vps.blocks).selection;
if ((objectB === "none") || (typeof objectB === "number")) {
return vps; return vps;
} }
const object = action.payload; const object = action.payload;
// check for pronoun conflict // check for pronoun conflict
if (hasPronounConflict(vps.subject, object)) { if (hasPronounConflict(subjectB, object)) {
if (sendAlert) sendAlert("That combination of pronouns is not allowed"); if (sendAlert) sendAlert("That combination of pronouns is not allowed");
return vps; return vps;
} }
return { return {
...vps, ...vps,
object, blocks: adjustObjectSelection(vps.blocks, object),
}; };
} }
if (action.type === "swap subj/obj") { if (action.type === "swap subj/obj") {
@ -114,6 +134,8 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
} }
if (action.type === "set voice") { if (action.type === "set voice") {
if (vps.verb && vps.verb.canChangeVoice) { if (vps.verb && vps.verb.canChangeVoice) {
const subject = getSubjectSelection(vps.blocks).selection;
const object = getObjectSelection(vps.blocks).selection;
const voice = action.payload; const voice = action.payload;
if (voice === "passive" && vps.verb.tenseCategory === "imperative") { if (voice === "passive" && vps.verb.tenseCategory === "imperative") {
return vps; return vps;
@ -121,8 +143,10 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
if (voice === "passive") { if (voice === "passive") {
return { return {
...vps, ...vps,
subject: typeof vps.object === "object" ? vps.object : undefined, blocks: adjustObjectSelection(
object: "none", adjustSubjectSelection(vps.blocks, typeof object === "object" ? object : undefined),
"none",
),
verb: { verb: {
...vps.verb, ...vps.verb,
voice, voice,
@ -131,8 +155,10 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
} else { } else {
return { return {
...vps, ...vps,
subject: undefined, blocks: adjustObjectSelection(
object: typeof vps.subject === "object" ? vps.subject : undefined, adjustSubjectSelection(vps.blocks, undefined),
typeof subject === "object" ? subject : undefined,
),
verb: { verb: {
...vps.verb, ...vps.verb,
voice, voice,
@ -225,9 +251,36 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
}, },
}; };
} }
// if (action.type === "set verb") { if (action.type === "set verb") {
return makeVPSelectionState(action.payload, vps); return makeVPSelectionState(action.payload, vps);
// } }
if (action.type === "insert new AP") {
return {
...vps,
blocks: insertNewAP(vps.blocks),
};
}
if (action.type === "set AP") {
const { index, AP } = action.payload;
return {
...vps,
blocks: setAP(vps.blocks, index, AP),
};
}
if (action.type === "remove AP") {
return {
...vps,
blocks: removeAP(vps.blocks, action.payload),
};
}
if (action.type === "shift block") {
const { index, direction } = action.payload;
return {
...vps,
blocks: shiftBlock(vps.blocks, index, direction),
};
}
throw new Error("unknown vpsReducer state");
} }
} }

View File

@ -54,8 +54,8 @@ export function randomSubjObj(old?: { subj: T.Person, obj?: T.Person }): { subj:
} }
export function getEnglishVerb(entry: T.DictionaryEntry): string { export function getEnglishVerb(entry: T.DictionaryEntry): string {
if (!entry.ec) { if (!entry.ec) {
console.log("errored verb"); console.error("errored verb");
console.log(entry); console.error(entry);
throw new Error("no english information for verb"); throw new Error("no english information for verb");
} }
if (entry.ep) { if (entry.ep) {

View File

@ -1,8 +1,8 @@
import * as T from "../../types"; import * as T from "../../types";
export function getSubjectSelection(blocks: T.EPSBlockComplete[]): T.SubjectSelectionComplete; export function getSubjectSelection(blocks: T.EPSBlockComplete[] | T.VPSBlockComplete[]): T.SubjectSelectionComplete;
export function getSubjectSelection(blocks: T.EPSBlock[]): T.SubjectSelection; export function getSubjectSelection(blocks: T.EPSBlock[] | T.VPSBlock[]): T.SubjectSelection;
export function getSubjectSelection(blocks: T.EPSBlock[] | T.EPSBlockComplete[]): T.SubjectSelection | T.SubjectSelectionComplete { export function getSubjectSelection(blocks: T.EPSBlock[] | T.EPSBlockComplete[] | T.VPSBlockComplete[] | T.VPSBlock[]): T.SubjectSelection | T.SubjectSelectionComplete {
const b = blocks.find(f => f.block?.type === "subjectSelection"); const b = blocks.find(f => f.block?.type === "subjectSelection");
if (!b || !b.block || b.block.type !== "subjectSelection") { if (!b || !b.block || b.block.type !== "subjectSelection") {
throw new Error("subjectSelection not found in blocks"); throw new Error("subjectSelection not found in blocks");
@ -10,14 +10,33 @@ export function getSubjectSelection(blocks: T.EPSBlock[] | T.EPSBlockComplete[])
return b.block; return b.block;
} }
export function getRenderedSubjectSelection(blocks: (T.Rendered<T.SubjectSelectionComplete> | T.Rendered<T.APSelection>)[]): T.Rendered<T.SubjectSelectionComplete> { export function getObjectSelection(blocks: T.VPSBlockComplete[]): T.ObjectSelectionComplete;
const b = blocks.find(f => f.type === "subjectSelection"); export function getObjectSelection(blocks: T.VPSBlock[]): T.ObjectSelection;
if (!b || b.type !== "subjectSelection") { export function getObjectSelection(blocks: T.VPSBlock[] | T.VPSBlockComplete[]): T.ObjectSelection | T.ObjectSelectionComplete {
const b = blocks.find(f => f.block?.type === "objectSelection");
if (!b || !b.block || b.block.type !== "objectSelection") {
throw new Error("objectSelection not found in blocks");
}
return b.block;
}
export function getRenderedSubjectSelection(blocks: (T.Rendered<T.SubjectSelectionComplete> | T.Rendered<T.NPSelection> | T.Rendered<T.APSelection> | T.RenderedVPSBlock)[]): T.Rendered<T.SubjectSelectionComplete> {
const b = blocks.find(f => typeof f === "object" && f.type === "subjectSelection");
if (!b || typeof b !== "object" || b.type !== "subjectSelection") {
throw new Error("subjectSelection not found in blocks"); throw new Error("subjectSelection not found in blocks");
} }
return b; return b;
} }
export function getRenderedObjectSelection(blocks: T.RenderedVPSBlock[]): T.Rendered<T.ObjectSelectionComplete> {
const b = blocks.find(f => typeof f === "object" && f.type === "objectSelection");
if (!b || typeof b !== "object" || b.type !== "objectSelection") {
throw new Error("objectSelection not found in blocks");
}
return b;
}
export function makeEPSBlocks(): T.EPSBlock[] { export function makeEPSBlocks(): T.EPSBlock[] {
return [ return [
{ {
@ -30,20 +49,33 @@ export function makeEPSBlocks(): T.EPSBlock[] {
]; ];
} }
export function makeAPBlock(): T.EPSBlock { export function makeAPBlock(): { key: number, block: undefined } {
return { return {
key: Math.random(), key: Math.random(),
block: undefined, block: undefined,
}; };
} }
export function makeSubjectSelection(selection: T.NPSelection | undefined): T.SubjectSelection { export function makeSubjectSelection(selection: T.SubjectSelection | T.NPSelection | undefined): T.SubjectSelection {
if (selection?.type === "subjectSelection") {
return selection;
}
return { return {
type: "subjectSelection", type: "subjectSelection",
selection, selection,
}; };
} }
export function makeObjectSelection(selection: T.ObjectSelection | T.ObjectNP | T.NPSelection | undefined): T.ObjectSelection {
if (selection && typeof selection === "object" && selection.type === "objectSelection") {
return selection;
}
return {
type: "objectSelection",
selection: selection,
};
}
export function EPSBlocksAreComplete(blocks: T.EPSBlock[]): blocks is T.EPSBlockComplete[] { export function EPSBlocksAreComplete(blocks: T.EPSBlock[]): blocks is T.EPSBlockComplete[] {
if (blocks.some(block => block.block === undefined)) { if (blocks.some(block => block.block === undefined)) {
return false; return false;
@ -52,9 +84,20 @@ export function EPSBlocksAreComplete(blocks: T.EPSBlock[]): blocks is T.EPSBlock
return !!subject.selection; return !!subject.selection;
} }
export function adjustSubjectSelection(blocks: T.EPSBlock[], subject: T.SubjectSelectionComplete | T.NPSelection): T.EPSBlockComplete[]; export function VPSBlocksAreComplete(blocks: T.VPSBlock[]): blocks is T.VPSBlockComplete[] {
export function adjustSubjectSelection(blocks: T.EPSBlock[], subject: T.SubjectSelection | undefined): T.EPSBlock[]; if (blocks.some(block => block.block === undefined)) {
export function adjustSubjectSelection(blocks: T.EPSBlock[], subject: T.SubjectSelection | T.SubjectSelectionComplete | T.NPSelection | undefined): T.EPSBlock[] | T.EPSBlockComplete[] { return false;
}
const subject = getSubjectSelection(blocks);
if (!subject.selection) return false;
const object = getObjectSelection(blocks);
if (!object.selection) return false;
return true;
}
export function adjustSubjectSelection(blocks: T.EPSBlock[], subject: T.SubjectSelection | T.NPSelection | undefined): T.EPSBlock[];
export function adjustSubjectSelection(blocks: T.VPSBlock[], subject: T.SubjectSelection | T.NPSelection | undefined): T.VPSBlock[];
export function adjustSubjectSelection(blocks: T.VPSBlock[] | T.EPSBlock[], subject: T.SubjectSelection | T.NPSelection | undefined): T.VPSBlock[] | T.EPSBlock[] {
const nb = [...blocks]; const nb = [...blocks];
const i = nb.findIndex(b => b.block && b.block.type === "subjectSelection"); const i = nb.findIndex(b => b.block && b.block.type === "subjectSelection");
if (i === -1) { if (i === -1) {
@ -63,3 +106,56 @@ export function adjustSubjectSelection(blocks: T.EPSBlock[], subject: T.SubjectS
nb[i].block = subject?.type === "subjectSelection" ? subject : makeSubjectSelection(subject); nb[i].block = subject?.type === "subjectSelection" ? subject : makeSubjectSelection(subject);
return nb; return nb;
} }
export function adjustObjectSelection(blocks: T.VPSBlock[], object: T.ObjectSelectionComplete | T.NPSelection | T.VerbObject | T.ObjectSelectionComplete): T.VPSBlockComplete[];
export function adjustObjectSelection(blocks: T.VPSBlock[], object: T.ObjectSelection | T.NPSelection | T.VerbObject | T.ObjectSelection | undefined): T.EPSBlock[];
export function adjustObjectSelection(blocks: T.VPSBlock[], object: T.ObjectSelection | T.ObjectSelectionComplete | T.VerbObject | T.NPSelection | undefined): T.VPSBlock[] | T.VPSBlockComplete[] {
const nb = [...blocks];
const i = nb.findIndex(b => b.block && b.block.type === "objectSelection");
if (i === -1) {
throw new Error("couldn't find objectSelection to modify");
}
nb[i].block = typeof object === "object" && object?.type === "objectSelection"
? object
: makeObjectSelection(object);
return nb;
}
export function shiftBlock<B extends T.VPSBlock[] | T.EPSBlock[]>(blocks: B, index: number, direction: "back" | "forward"): B {
const newIndex = index + (direction === "forward"
? 1 // (isNoObject(blocks[index + 1].block) ? 2 : 1)
: -1 // (isNoObject(blocks[index - 1].block) ? -2 : -2)
);
return arrayMove(blocks, index, newIndex) as B;
}
export function insertNewAP<B extends T.VPSBlock[] | T.EPSBlock[]>(blocks: B): B {
return [makeAPBlock(), ...blocks] as B;
}
export function setAP<B extends T.VPSBlock[] | T.EPSBlock[]>(blocks: B, index: number, AP: T.APSelection | undefined): B {
const nBlocks = [...blocks] as B;
nBlocks[index].block = AP;
return nBlocks;
}
export function removeAP<B extends T.VPSBlock[] | T.EPSBlock[]>(blocks: B, index: number): B {
const nBlocks = [...blocks] as B;
blocks.splice(index, 1);
return nBlocks;
}
export function isNoObject(b: T.VPSBlock["block"] | T.EPSBlock["block"]): b is { type: "objectSelection", selection: "none" } {
return !!(b && b.type === "objectSelection" && b.selection === "none");
}
function arrayMove<X>(ar: X[], old_index: number, new_index: number): X[] {
const arr = [...ar];
const new_i = (new_index >= arr.length)
? (arr.length - 1)
: (new_index < 0)
? 0
: new_index;
arr.splice(new_i, 0, arr.splice(old_index, 1)[0]);
return arr;
};

View File

@ -25,7 +25,7 @@ import { pronouns } from "../grammar-units";
import { completeEPSelection, renderEP } from "./render-ep"; import { completeEPSelection, renderEP } from "./render-ep";
import { completeVPSelection } from "./vp-tools"; import { completeVPSelection } from "./vp-tools";
import { renderVP } from "./render-vp"; import { renderVP } from "./render-vp";
import { getRenderedSubjectSelection } from "./blocks-utils"; import { getRenderedObjectSelection, getRenderedSubjectSelection } from "./blocks-utils";
const blank: T.PsString = { const blank: T.PsString = {
p: "______", p: "______",
@ -35,14 +35,13 @@ type BlankoutOptions = { equative?: boolean, ba?: boolean, kidsSection?: boolean
const kidsBlank = makeSegment({ p: "___", f: "___" }, ["isKid"]); const kidsBlank = makeSegment({ p: "___", f: "___" }, ["isKid"]);
type Form = T.FormVersion & { OSV?: boolean }; export function compileVP(VP: T.VPRendered, form: T.FormVersion): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] };
export function compileVP(VP: T.VPRendered, form: Form): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] }; export function compileVP(VP: T.VPRendered, form: T.FormVersion, combineLengths: true): { ps: T.PsString[], e?: string [] };
export function compileVP(VP: T.VPRendered, form: Form, combineLengths: true): { ps: T.PsString[], e?: string [] }; export function compileVP(VP: T.VPRendered, form: T.FormVersion, combineLengths?: true): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] } {
export function compileVP(VP: T.VPRendered, form: Form, combineLengths?: true): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] } {
const verb = VP.verb.ps; const verb = VP.verb.ps;
const { kids, NPs } = getVPSegmentsAndKids(VP, form); const { kids, blocks } = getVPSegmentsAndKids(VP, form);
const psResult = compilePs({ const psResult = compilePs({
NPs, blocks,
kids, kids,
verb, verb,
VP, VP,
@ -50,12 +49,12 @@ export function compileVP(VP: T.VPRendered, form: Form, combineLengths?: true):
return { return {
ps: combineLengths ? flattenLengths(psResult) : psResult, ps: combineLengths ? flattenLengths(psResult) : psResult,
// TODO: English doesn't quite work for dynamic compounds in passive voice // TODO: English doesn't quite work for dynamic compounds in passive voice
e: (VP.verb.voice === "passive" && VP.isCompound === "dynamic") ? undefined : compileEnglish(VP), e: (VP.verb.voice === "passive" && VP.isCompound === "dynamic") ? undefined : compileEnglishVP(VP),
}; };
} }
type CompilePsInput = { type CompilePsInput = {
NPs: Segment[][], blocks: Segment[],
kids: Segment[], kids: Segment[],
verb: { verb: {
head: T.PsString | undefined, head: T.PsString | undefined,
@ -64,13 +63,13 @@ type CompilePsInput = {
VP: T.VPRendered, VP: T.VPRendered,
} }
function compilePs({ NPs, kids, verb: { head, rest }, VP }: CompilePsInput): T.SingleOrLengthOpts<T.PsString[]> { function compilePs({ blocks, kids, verb: { head, rest }, VP }: CompilePsInput): T.SingleOrLengthOpts<T.PsString[]> {
if ("long" in rest) { if ("long" in rest) {
return { return {
long: compilePs({ NPs, verb: { head, rest: rest.long }, VP, kids }) as T.PsString[], long: compilePs({ blocks, verb: { head, rest: rest.long }, VP, kids }) as T.PsString[],
short: compilePs({ NPs, verb: { head, rest: rest.short }, VP, kids }) as T.PsString[], short: compilePs({ blocks, verb: { head, rest: rest.short }, VP, kids }) as T.PsString[],
...rest.mini ? { ...rest.mini ? {
mini: compilePs({ NPs, verb: { head, rest: rest.mini }, VP, kids }) as T.PsString[], mini: compilePs({ blocks, verb: { head, rest: rest.mini }, VP, kids }) as T.PsString[],
} : {}, } : {},
}; };
} }
@ -78,55 +77,62 @@ function compilePs({ NPs, kids, verb: { head, rest }, VP }: CompilePsInput): T.S
// put together all the different possible permutations based on: // put together all the different possible permutations based on:
// a. potential different versions of where the nu goes // a. potential different versions of where the nu goes
return removeDuplicates(verbWNegativeVersions.flatMap((verbSegments) => ( return removeDuplicates(verbWNegativeVersions.flatMap((verbSegments) => {
// b. potential reordering of NPs
NPs.flatMap(NP => {
// for each permutation of the possible ordering of NPs and Verb + nu // for each permutation of the possible ordering of NPs and Verb + nu
// 1. put in kids in the kids section // 1. put in kids in the kids section
const segments = putKidsInKidsSection([...NP, ...verbSegments], kids); const segments = putKidsInKidsSection([...blocks, ...verbSegments], kids);
// 2. space out the words properly // 2. space out the words properly
const withProperSpaces = addSpacesBetweenSegments(segments); const withProperSpaces = addSpacesBetweenSegments(segments);
// 3. throw it all together into a PsString for each permutation // 3. throw it all together into a PsString for each permutation
return combineSegments(withProperSpaces); return combineSegments(withProperSpaces);
}) }));
)));
} }
export function getShrunkenServant(VP: T.VPRendered): Segment | undefined { export function getShrunkenServant(VP: T.VPRendered): Segment | undefined {
const shrinkServant = VP.form.shrinkServant && !(VP.isCompound === "dynamic" && !VP.isPast); const shrinkServant = VP.form.shrinkServant && !(VP.isCompound === "dynamic" && !VP.isPast);
const toShrinkServant = (() => { const toShrinkServant: T.Rendered<T.NPSelection> | undefined = (() => {
if (!shrinkServant) return undefined; if (!shrinkServant) return undefined;
if (!VP.servant) return undefined; if (!VP.servant) return undefined;
const servant = VP[VP.servant]; const servant = VP.servant === "subject"
? getRenderedSubjectSelection(VP.blocks).selection
: getRenderedObjectSelection(VP.blocks).selection;
if (typeof servant !== "object") return undefined; if (typeof servant !== "object") return undefined;
return servant; return servant;
})(); })();
return toShrinkServant ? shrinkNP(toShrinkServant) : undefined; return toShrinkServant ? shrinkNP(toShrinkServant) : undefined;
} }
export function getVPSegmentsAndKids(VP: T.VPRendered, form?: Form): { kids: Segment[], NPs: Segment[][] } { export function getVPSegmentsAndKids(VP: T.VPRendered, form?: T.FormVersion): { kids: Segment[], blocks: Segment[] } {
const removeKing = VP.form.removeKing && !(VP.isCompound === "dynamic" && VP.isPast); const removeKing = VP.form.removeKing && !(VP.isCompound === "dynamic" && VP.isPast);
const shrunkenServant = getShrunkenServant(VP); const shrunkenServant = getShrunkenServant(VP);
const possToShrink = findPossesivesToShrinkInVP(VP, { const possToShrink = findPossesivesToShrinkInVP(VP, {
shrunkServant: !!shrunkenServant, shrunkServant: !!shrunkenServant,
removedKing: removeKing, removedKing: removeKing,
}); });
const SO = { const subject = getRenderedSubjectSelection(VP.blocks).selection;
subject: getPashtoFromRendered(VP.subject, false), const blocks = VP.blocks.reduce((accum, block) => {
object: typeof VP.object === "object" ? getPashtoFromRendered(VP.object, VP.subject.person) : undefined, if (block.type === "subjectSelection") {
}; if (VP.servant === "subject" && shrunkenServant) return accum;
function getSegment(t: "subject" | "object"): Segment | undefined { if (VP.king === "subject" && removeKing) return accum;
const word = (VP.servant === t) return [
? (!shrunkenServant ? SO[t] : undefined) ...accum,
: (VP.king === t) makeSegment(getPashtoFromRendered(block.selection, false)),
? (!removeKing ? SO[t] : undefined) ];
: undefined;
if (!word) return undefined;
return makeSegment(word);
} }
const subject = getSegment("subject"); if (block.type === "objectSelection") {
const object = getSegment("object"); if (VP.servant === "object" && shrunkenServant) return accum;
if (VP.king === "object" && removeKing) return accum;
if (typeof block.selection !== "object") return accum;
return [
...accum,
makeSegment(getPashtoFromRendered(block.selection, subject.person)),
]
}
return [
...accum,
makeSegment(getPashtoFromRendered(block, false)),
];
}, [] as Segment[]);
return { return {
kids: orderKidsSection([ kids: orderKidsSection([
...VP.verb.hasBa ...VP.verb.hasBa
@ -135,14 +141,37 @@ export function getVPSegmentsAndKids(VP: T.VPRendered, form?: Form): { kids: Seg
? [shrunkenServant] : [], ? [shrunkenServant] : [],
...possToShrink.map(shrinkNP), ...possToShrink.map(shrinkNP),
]), ]),
NPs: [ blocks: blocks,
[ };
...subject ? [subject] : [], }
...object ? [object] : [],
], export function getEPSegmentsAndKids(EP: T.EPRendered, blankOut?: BlankoutOptions): { kids: Segment[], blocks: Segment[] } {
// TODO: make this an option to also include O S V order ?? const possToShrink = findPossesivesToShrinkInEP(EP);
// also show O S V if both are showing const blocks = EP.blocks.reduce((accum, block) => {
...(subject && object && (form && form.OSV)) ? [[object, subject]] : [], if (block.type === "subjectSelection") {
if (EP.omitSubject) return accum;
return [
...accum,
makeSegment(getPashtoFromRendered(block.selection, false)),
];
}
return [
...accum,
makeSegment(getPashtoFromRendered(block, false)),
];
}, [] as Segment[]);
const predicate = makeSegment(getPashtoFromRendered(EP.predicate, false));
return {
kids: blankOut?.kidsSection ? [kidsBlank] : orderKidsSection([
...EP.equative.hasBa
? (
blankOut?.ba ? [kidsBlank] : [makeSegment(grammarUnits.baParticle, ["isBa", "isKid"])])
: [],
...possToShrink.map(a => shrinkNP(a.np)),
]),
blocks: [
...blocks,
predicate
], ],
}; };
} }
@ -263,65 +292,34 @@ function arrangeVerbWNegative(head: T.PsString | undefined, restRaw: T.PsString[
export function compileEP(EP: T.EPRendered): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string[] }; export function compileEP(EP: T.EPRendered): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string[] };
export function compileEP(EP: T.EPRendered, combineLengths: true, blankOut?: BlankoutOptions): { ps: T.PsString[], e?: string[] }; export function compileEP(EP: T.EPRendered, combineLengths: true, blankOut?: BlankoutOptions): { ps: T.PsString[], e?: string[] };
export function compileEP(EP: T.EPRendered, combineLengths?: boolean, blankOut?: BlankoutOptions): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string[] } { export function compileEP(EP: T.EPRendered, combineLengths?: boolean, blankOut?: BlankoutOptions): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string[] } {
const { kids, NPs } = getEPSegmentsAndKids(EP, blankOut); const { kids, blocks } = getEPSegmentsAndKids(EP, blankOut);
const equative = EP.equative.ps; const equative = EP.equative.ps;
const psResult = compileEPPs({ const psResult = compileEPPs({
NPs, blocks,
kids, kids,
equative: blankOut?.equative ? [blank] : equative, equative: blankOut?.equative ? [blank] : equative,
negative: EP.equative.negative, negative: EP.equative.negative,
}); });
return { return {
ps: combineLengths ? flattenLengths(psResult) : psResult, ps: combineLengths ? flattenLengths(psResult) : psResult,
e: compileEPEnglish(EP), e: compileEnglishEP(EP),
}; };
} }
export function getEPSegmentsAndKids(EP: T.EPRendered, blankOut?: BlankoutOptions): { kids: Segment[], NPs: Segment[] } { function compileEPPs({ blocks, kids, equative, negative }: {
const possToShrink = findPossesivesToShrinkInEP(EP); blocks: Segment[],
const blocks = EP.blocks.reduce((accum, block) => {
if (block.type === "subjectSelection") {
if (EP.omitSubject) return accum;
return [
...accum,
makeSegment(getPashtoFromRendered(block.selection, false)),
];
}
return [
...accum,
makeSegment(getPashtoFromRendered(block, false)),
];
}, [] as Segment[]);
const predicate = makeSegment(getPashtoFromRendered(EP.predicate, false));
return {
kids: blankOut?.kidsSection ? [kidsBlank] : orderKidsSection([
...EP.equative.hasBa
? (
blankOut?.ba ? [kidsBlank] : [makeSegment(grammarUnits.baParticle, ["isBa", "isKid"])])
: [],
...possToShrink.map(a => shrinkNP(a.np)),
]),
NPs: [
...blocks,
predicate
],
};
}
function compileEPPs({ NPs, kids, equative, negative }: {
NPs: Segment[],
kids: Segment[], kids: Segment[],
equative: T.SingleOrLengthOpts<T.PsString[]>, equative: T.SingleOrLengthOpts<T.PsString[]>,
negative: boolean, negative: boolean,
}): T.SingleOrLengthOpts<T.PsString[]> { }): T.SingleOrLengthOpts<T.PsString[]> {
if ("long" in equative) { if ("long" in equative) {
return { return {
long: compileEPPs({ NPs, kids, equative: equative.long, negative }) as T.PsString[], long: compileEPPs({ blocks, kids, equative: equative.long, negative }) as T.PsString[],
short: compileEPPs({ NPs, kids, equative: equative.short, negative }) as T.PsString[], short: compileEPPs({ blocks, kids, equative: equative.short, negative }) as T.PsString[],
}; };
} }
const allSegments = putKidsInKidsSection([ const allSegments = putKidsInKidsSection([
...NPs, ...blocks,
...negative ? [ ...negative ? [
makeSegment({ p: "نه", f: "nú" }), makeSegment({ p: "نه", f: "nú" }),
makeSegment(removeAccents(equative)) makeSegment(removeAccents(equative))
@ -332,25 +330,7 @@ function compileEPPs({ NPs, kids, equative, negative }: {
return removeDuplicates(combineSegments(allSegments, "spaces")); return removeDuplicates(combineSegments(allSegments, "spaces"));
} }
function compileEPEnglish(EP: T.EPRendered): string[] | undefined { function getEnglishAPs(blocks: (T.Rendered<T.SubjectSelectionComplete> | T.Rendered<T.APSelection> | T.RenderedVPSBlock)[]) {
function insertEWords(e: string, { subject, predicate, APs }: { subject: string, predicate: string, APs: string }): string {
return e.replace("$SUBJ", subject).replace("$PRED", predicate || "") + APs;
}
const engSubj = getRenderedSubjectSelection(EP.blocks).selection.e;
const engPred = getEnglishFromRendered(EP.predicate);
const engAPs = getEnglishAPs(EP.blocks);
// require all English parts for making the English phrase
const b = (EP.englishBase && engSubj && engPred)
? EP.englishBase.map(e => insertEWords(e, {
subject: engSubj,
predicate: engPred,
APs: engAPs,
}))
: undefined;
return b;
}
function getEnglishAPs(blocks: (T.Rendered<T.SubjectSelectionComplete> | T.Rendered<T.APSelection>)[]) {
const APs = blocks.filter(x => x.type !== "subjectSelection") as T.Rendered<T.APSelection>[]; const APs = blocks.filter(x => x.type !== "subjectSelection") as T.Rendered<T.APSelection>[];
return APs.reduce((accum, curr) => { return APs.reduce((accum, curr) => {
const e = getEnglishFromRendered(curr); const e = getEnglishFromRendered(curr);
@ -398,23 +378,42 @@ function addSpacesBetweenSegments(segments: Segment[]): (Segment | " " | "" | T.
return o; return o;
} }
function compileEnglish(VP: T.VPRendered): string[] | undefined { function compileEnglishVP(VP: T.VPRendered): string[] | undefined {
function insertEWords(e: string, { subject, object }: { subject: string, object?: string }): string { function insertEWords(e: string, { subject, object, APs }: { subject: string, object?: string, APs: string }): string {
return e.replace("$SUBJ", subject).replace("$OBJ", object || ""); return e.replace("$SUBJ", subject).replace("$OBJ", object || "") + APs;
} }
const engSubj = getEnglishFromRendered(VP.subject); const engSubj = getRenderedSubjectSelection(VP.blocks).selection;
const engObj = typeof VP.object === "object" const obj = getRenderedObjectSelection(VP.blocks).selection;
? getEnglishFromRendered(VP.object) const engObj = typeof obj === "object" ? obj.e : "";
: undefined; const engAPs = getEnglishAPs(VP.blocks);
// require all English parts for making the English phrase // require all English parts for making the English phrase
return (VP.englishBase && engSubj && (engObj || typeof VP.object !== "object")) return (VP.englishBase && engSubj && engObj !== undefined)
? VP.englishBase.map(e => insertEWords(e, { ? VP.englishBase.map(e => insertEWords(e, {
subject: engSubj, // TODO: make sure we actually have the english
object: engObj, subject: getEnglishFromRendered(engSubj) || "",
object: getEnglishFromRendered(engSubj) || "",
APs: engAPs,
})) }))
: undefined; : undefined;
} }
function compileEnglishEP(EP: T.EPRendered): string[] | undefined {
function insertEWords(e: string, { subject, predicate, APs }: { subject: string, predicate: string, APs: string }): string {
return e.replace("$SUBJ", subject).replace("$PRED", predicate || "") + APs;
}
const engSubj = getRenderedSubjectSelection(EP.blocks).selection.e;
const engPred = getEnglishFromRendered(EP.predicate);
const engAPs = getEnglishAPs(EP.blocks);
// require all English parts for making the English phrase
const b = (EP.englishBase && engSubj && engPred)
? EP.englishBase.map(e => insertEWords(e, {
subject: engSubj,
predicate: engPred,
APs: engAPs,
}))
: undefined;
return b;
}
export function orderKidsSection(kids: Segment[]): Segment[] { export function orderKidsSection(kids: Segment[]): Segment[] {
const sorted = [...kids]; const sorted = [...kids];
@ -472,19 +471,34 @@ export function findPossesivesToShrinkInVP(VP: T.VPRendered, f: {
shrunkServant: boolean, shrunkServant: boolean,
removedKing: boolean, removedKing: boolean,
}): T.Rendered<T.NPSelection>[] { }): T.Rendered<T.NPSelection>[] {
return findPossesives(VP.subject, VP.object).filter(x => ( return VP.blocks.reduce((found, block) => {
// only give the possesive to shrink if it's not alread in a shrunken servant if (block.type === "subjectSelection") {
!(f.shrunkServant && x.role === "servant") if (block.selection.role === "king" && f.removedKing) return found;
&& // or in a removed king if (block.selection.role === "servant" && f.shrunkServant) return found;
!(f.removedKing && x.role === "king") return [
)); ...findPossesivesInNP(block.selection),
...found,
];
} }
if (block.type === "objectSelection") {
function findPossesives(...nps: (T.Rendered<T.NPSelection> | T.ObjectNP | undefined)[]): T.Rendered<T.NPSelection>[] { if (typeof block.selection !== "object") return found;
return nps.reduce((accum, curr) => { if (block.selection.role === "king" && f.removedKing) return found;
const res = findPossesivesInNP(curr); if (block.selection.role === "servant" && f.shrunkServant) return found;
if (res) return [...accum, ...res]; return [
return accum; ...findPossesivesInNP(block.selection),
...found,
];
}
if (block.type === "sandwich") {
if (block.inside.type === "pronoun") {
return found;
}
return [
...findPossesivesInNP(block.inside),
...found,
];
}
return found;
}, [] as T.Rendered<T.NPSelection>[]); }, [] as T.Rendered<T.NPSelection>[]);
} }

View File

@ -206,7 +206,6 @@ export function getEnglishFromRendered(r: T.Rendered<T.NPSelection | T.EqCompSel
const np = r as T.Rendered<T.NounSelection>; const np = r as T.Rendered<T.NounSelection>;
return addPossesors(np.possesor?.np, addArticlesAndAdjs(np), r.type); return addPossesors(np.possesor?.np, addArticlesAndAdjs(np), r.type);
} }
// TODO: possesives in English for participles and pronouns too!
return r.e; return r.e;
} }

View File

@ -84,10 +84,9 @@ function renderEquative(es: T.EquativeSelection, person: T.Person): T.EquativeRe
}; };
} }
function renderAdverbSelection(a: T.AdverbSelection): T.Rendered<T.AdverbSelection> { export function renderAdverbSelection(a: T.AdverbSelection): T.Rendered<T.AdverbSelection> {
const e = getEnglishWord(a.entry); const e = getEnglishWord(a.entry);
if (!e || typeof e !== "string") { if (!e || typeof e !== "string") {
console.log(e);
throw new Error("error getting english for compliment"); throw new Error("error getting english for compliment");
} }
return { return {
@ -104,7 +103,6 @@ function renderEqCompSelection(s: T.EqCompSelection, person: T.Person): T.Render
} }
const e = getEnglishWord(s.entry); const e = getEnglishWord(s.entry);
if (!e || typeof e !== "string") { if (!e || typeof e !== "string") {
console.log(e);
throw new Error("error getting english for compliment"); throw new Error("error getting english for compliment");
} }
if (isLocativeAdverbEntry(s.entry)) { if (isLocativeAdverbEntry(s.entry)) {

View File

@ -22,26 +22,31 @@ import {
import { renderEnglishVPBase } from "./english-vp-rendering"; import { renderEnglishVPBase } from "./english-vp-rendering";
import { personGender } from "../../lib/misc-helpers"; import { personGender } from "../../lib/misc-helpers";
import { renderNPSelection } from "./render-np"; import { renderNPSelection } from "./render-np";
import { getObjectSelection, getSubjectSelection } from "./blocks-utils";
import { renderSandwich } from "./render-sandwich";
import { renderAdverbSelection } from "./render-ep";
// TODO: ISSUE GETTING SPLIT HEAD NOT MATCHING WITH FUTURE VERBS // TODO: ISSUE GETTING SPLIT HEAD NOT MATCHING WITH FUTURE VERBS
export function renderVP(VP: T.VPSelectionComplete): T.VPRendered { export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
const subject = getSubjectSelection(VP.blocks).selection;
const object = getObjectSelection(VP.blocks).selection;
// Sentence Rules Logic // Sentence Rules Logic
const isPast = isPastTense(VP.verb.tense); const isPast = isPastTense(VP.verb.tense);
const isTransitive = VP.object !== "none"; const isTransitive = object !== "none";
const { king, servant } = getKingAndServant(isPast, isTransitive); const { king, servant } = getKingAndServant(isPast, isTransitive);
const kingPerson = getPersonFromNP( const kingPerson = getPersonFromNP(
king === "subject" ? VP.subject : VP.object, king === "subject" ? subject : object,
); );
// TODO: more elegant way of handling this type safety // TODO: more elegant way of handling this type safety
if (kingPerson === undefined) { if (kingPerson === undefined) {
throw new Error("king of sentance does not exist"); throw new Error("king of sentance does not exist");
} }
const subjectPerson = getPersonFromNP(VP.subject); const subjectPerson = getPersonFromNP(subject);
const objectPerson = getPersonFromNP(VP.object); const objectPerson = getPersonFromNP(object);
// TODO: also don't inflect if it's a pattern one animate noun // TODO: also don't inflect if it's a pattern one animate noun
const inflectSubject = isPast && isTransitive && !isMascSingAnimatePattern4(VP.subject); const inflectSubject = isPast && isTransitive && !isMascSingAnimatePattern4(subject);
const inflectObject = !isPast && isFirstOrSecondPersPronoun(VP.object); const inflectObject = !isPast && isFirstOrSecondPersPronoun(object);
// Render Elements // Render Elements
const b: T.VPRendered = { const b: T.VPRendered = {
type: "VPRendered", type: "VPRendered",
@ -50,12 +55,15 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
isPast, isPast,
isTransitive, isTransitive,
isCompound: VP.verb.isCompound, isCompound: VP.verb.isCompound,
subject: renderNPSelection(VP.subject, inflectSubject, false, "subject", king === "subject" ? "king" : "servant"), blocks: renderVPBlocks(VP.blocks, {
object: renderNPSelection(VP.object, inflectObject, true, "object", king === "object" ? "king" : "servant"), inflectSubject,
inflectObject,
king,
}),
verb: renderVerbSelection(VP.verb, kingPerson, objectPerson), verb: renderVerbSelection(VP.verb, kingPerson, objectPerson),
englishBase: renderEnglishVPBase({ englishBase: renderEnglishVPBase({
subjectPerson, subjectPerson,
object: VP.verb.isCompound === "dynamic" ? "none" : VP.object, object: VP.verb.isCompound === "dynamic" ? "none" : object,
vs: VP.verb, vs: VP.verb,
}), }),
form: VP.form, form: VP.form,
@ -64,6 +72,35 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
return b; return b;
} }
function renderVPBlocks(blocks: T.VPSBlockComplete[], config: {
inflectSubject: boolean,
inflectObject: boolean,
king: "subject" | "object",
}): T.RenderedVPSBlock[] {
return blocks.map(({ block }): T.RenderedVPSBlock => {
if (block.type === "sandwich") {
return renderSandwich(block);
}
if (block.type === "adverb") {
return renderAdverbSelection(block);
}
if (block.type === "subjectSelection") {
return {
type: "subjectSelection",
selection: renderNPSelection(block.selection, config.inflectSubject, false, "subject", config.king === "subject" ? "king" : "servant"),
}
}
// if (block.type === "objectSelection") {
const object = typeof block === "object" ? block.selection : block;
const selection = renderNPSelection(object, config.inflectObject, true, "object", config.king === "object" ? "king" : "servant");
return {
type: "objectSelection",
selection,
};
// }
});
}
function whatsAdjustable(VP: T.VPSelectionComplete): "both" | "king" | "servant" { function whatsAdjustable(VP: T.VPSelectionComplete): "both" | "king" | "servant" {
// TODO: intransitive dynamic compounds? // TODO: intransitive dynamic compounds?
return (VP.verb.isCompound === "dynamic" && VP.verb.transitivity === "transitive") return (VP.verb.isCompound === "dynamic" && VP.verb.transitivity === "transitive")

View File

@ -7,6 +7,7 @@ import {
import { isImperativeTense, isPerfectTense } from "../type-predicates"; import { isImperativeTense, isPerfectTense } from "../type-predicates";
import * as grammarUnits from "../../lib/grammar-units"; import * as grammarUnits from "../../lib/grammar-units";
import { randomNumber } from "../../lib/misc-helpers"; import { randomNumber } from "../../lib/misc-helpers";
import { adjustObjectSelection, adjustSubjectSelection, getObjectSelection, getSubjectSelection, VPSBlocksAreComplete } from "./blocks-utils";
export function isInvalidSubjObjCombo(subj: T.Person, obj: T.Person): boolean { export function isInvalidSubjObjCombo(subj: T.Person, obj: T.Person): boolean {
const firstPeople = [ const firstPeople = [
@ -193,34 +194,35 @@ export function removeDuplicates(psv: T.PsString[]): T.PsString[] {
)); ));
} }
export function switchSubjObj(vps: T.VPSelectionState): T.VPSelectionState; export function switchSubjObj<V extends T.VPSelectionState | T.VPSelectionComplete>(vps: V): V {
export function switchSubjObj(vps: T.VPSelectionComplete): T.VPSelectionComplete; const subject = getSubjectSelection(vps.blocks).selection;
export function switchSubjObj(vps: T.VPSelectionState | T.VPSelectionComplete): T.VPSelectionState | T.VPSelectionComplete { const object = getObjectSelection(vps.blocks).selection;
if ("tenseCategory" in vps.verb) { if ("tenseCategory" in vps.verb) {
if (!vps.subject || !(typeof vps.object === "object") || (vps.verb.tenseCategory === "imperative")) { if (!subject || !(typeof object === "object") || (vps.verb.tenseCategory === "imperative")) {
return vps; return vps;
} }
return { return {
...vps, ...vps,
subject: vps.object, blocks: adjustObjectSelection(
object: vps.subject, adjustSubjectSelection(vps.blocks, object),
subject,
),
}; };
} }
if (!vps.subject|| !vps.verb || !(typeof vps.object === "object")) { if (!subject|| !vps.verb || !(typeof object === "object")) {
return vps; return vps;
} }
return { return {
...vps, ...vps,
subject: vps.object, blocks: adjustObjectSelection(
object: vps.subject, adjustSubjectSelection(vps.blocks, object),
subject,
),
}; };
} }
export function completeVPSelection(vps: T.VPSelectionState): T.VPSelectionComplete | undefined { export function completeVPSelection(vps: T.VPSelectionState): T.VPSelectionComplete | undefined {
if (vps.subject === undefined) { if (!VPSBlocksAreComplete(vps.blocks)) {
return undefined;
}
if (vps.object === undefined) {
return undefined; return undefined;
} }
// necessary for this version on typscript ... // necessary for this version on typscript ...
@ -228,13 +230,10 @@ export function completeVPSelection(vps: T.VPSelectionState): T.VPSelectionCompl
...vps.verb, ...vps.verb,
tense: getTenseFromVerbSelection(vps.verb), tense: getTenseFromVerbSelection(vps.verb),
}; };
const subject = vps.subject;
const object = vps.object
return { return {
...vps, ...vps,
subject,
object,
verb, verb,
blocks: vps.blocks,
}; };
} }
@ -257,12 +256,12 @@ export function isThirdPerson(p: T.Person): boolean {
} }
export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState): T.VPSelectionState { export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState): T.VPSelectionState {
console.log("checking more...", vps); const subject = getSubjectSelection(vps.blocks).selection;
const subjIs2ndPerson = (vps.subject?.type === "pronoun") && isSecondPerson(vps.subject.person); const object = getObjectSelection(vps.blocks).selection;
const objIs2ndPerson = (typeof vps.object === "object") const subjIs2ndPerson = (subject?.type === "pronoun") && isSecondPerson(subject.person);
&& (vps.object.type === "pronoun") const objIs2ndPerson = (typeof object === "object")
&& isSecondPerson(vps.object.person); && (object.type === "pronoun")
console.log({ subjIs2ndPerson, objIs2ndPerson }); && isSecondPerson(object.person);
const default2ndPersSubject: T.PronounSelection = { const default2ndPersSubject: T.PronounSelection = {
type: "pronoun", type: "pronoun",
distance: "far", distance: "far",
@ -279,41 +278,39 @@ export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState):
return vps; return vps;
} }
if (subjIs2ndPerson && objIs2ndPerson) { if (subjIs2ndPerson && objIs2ndPerson) {
if (typeof vps.object !== "object" || vps.object.type !== "pronoun") { if (typeof object !== "object" || object.type !== "pronoun") {
return vps; return vps;
} }
return { return {
...vps, ...vps,
object: { blocks: adjustObjectSelection(vps.blocks, {
...vps.object, ...object,
person: getNon2ndPersPronoun(), person: getNon2ndPersPronoun(),
}, }),
}; };
} }
if (!subjIs2ndPerson && objIs2ndPerson) { if (!subjIs2ndPerson && objIs2ndPerson) {
if (typeof vps.object !== "object" || vps.object.type !== "pronoun") { if (typeof object !== "object" || object.type !== "pronoun") {
return { return {
...vps, ...vps,
subject: default2ndPersSubject, blocks: adjustSubjectSelection(vps.blocks, default2ndPersSubject),
}; };
} }
return { return {
...vps, ...vps,
subject: default2ndPersSubject, blocks: adjustObjectSelection(
object: { adjustSubjectSelection(vps.blocks, default2ndPersSubject),
...vps.object, {
...object,
person: getNon2ndPersPronoun(), person: getNon2ndPersPronoun(),
}, },
),
}; };
} }
if (!subjIs2ndPerson && !objIs2ndPerson) { if (!subjIs2ndPerson && !objIs2ndPerson) {
console.log("returning last");
return { return {
...vps, ...vps,
subject: default2ndPersSubject, blocks: adjustSubjectSelection(vps.blocks, default2ndPersSubject),
verb: {
...vps.verb,
},
}; };
} }
throw new Error("error ensuring compatible VPSelection for imperative verb"); throw new Error("error ensuring compatible VPSelection for imperative verb");

View File

@ -98,6 +98,12 @@ export const sandwiches: T.Sandwich[] = [
after: { p: "په حیث", f: "pu heys" }, after: { p: "په حیث", f: "pu heys" },
e: "as", e: "as",
}, },
{
type: "sandwich",
before: { p: "د", f: "du" },
after: { p: "په لور", f: "pu lor" },
e: "towards",
},
]; ];
export default sandwiches; export default sandwiches;

File diff suppressed because one or more lines are too long

View File

@ -497,7 +497,7 @@ export type Pattern6FemEntry<T extends FemNounEntry> = T & { __brand3: "non anim
export type NonInflecting<T> = T & { __brand3: "non-inflecting" }; export type NonInflecting<T> = T & { __brand3: "non-inflecting" };
export type Entry = NounEntry | AdjectiveEntry | AdverbEntry | VerbEntry; export type Entry = NounEntry | AdjectiveEntry | AdverbEntry | VerbEntry;
export type RenderedVPSBlock = (Rendered<SubjectSelectionComplete> | Rendered<ObjectSelectionComplete> | Rendered<APSelection>);
// TODO: make this Rendered<VPSelectionComplete> with recursive Rendered<> // TODO: make this Rendered<VPSelectionComplete> with recursive Rendered<>
export type VPRendered = { export type VPRendered = {
type: "VPRendered", type: "VPRendered",
@ -506,8 +506,7 @@ export type VPRendered = {
isPast: boolean, isPast: boolean,
isTransitive: boolean, isTransitive: boolean,
isCompound: "stative" | "dynamic" | false, isCompound: "stative" | "dynamic" | false,
subject: Rendered<NPSelection>, blocks: RenderedVPSBlock[],
object: Rendered<NPSelection> | ObjectNP,
verb: VerbRendered, verb: VerbRendered,
englishBase?: string[], englishBase?: string[],
form: FormVersion, form: FormVersion,
@ -551,17 +550,13 @@ export type ObjectSelectionComplete = {
}; };
export type VPSelectionState = { export type VPSelectionState = {
// blocks: (SubjectSelection | ObjectSelection)[] blocks: VPSBlock[]
subject: NPSelection | undefined,
object: NPSelection | ObjectNP | undefined,
verb: VerbSelection, verb: VerbSelection,
form: FormVersion, form: FormVersion,
}; };
export type VPSelectionComplete = { export type VPSelectionComplete = {
// blocks: (SubjectSelectionComplete | ObjectSelectionComplete)[] blocks: VPSBlockComplete[]
subject: NPSelection,
object: NPSelection | ObjectNP,
verb: VerbSelectionComplete, verb: VerbSelectionComplete,
form: FormVersion, form: FormVersion,
}; };
@ -676,7 +671,7 @@ export type RenderedPossesorSelection = {
shrunken: boolean, shrunken: boolean,
}; };
export type Rendered<T extends NPSelection | EqCompSelection | AdjectiveSelection | SandwichSelection<Sandwich> | APSelection | SubjectSelectionComplete> = T extends SandwichSelection<Sandwich> export type Rendered<T extends NPSelection | EqCompSelection | AdjectiveSelection | SandwichSelection<Sandwich> | APSelection | SubjectSelectionComplete | ObjectSelectionComplete> = T extends SandwichSelection<Sandwich>
? Omit<SandwichSelection<Sandwich>, "inside"> & { ? Omit<SandwichSelection<Sandwich>, "inside"> & {
inside: Rendered<NPSelection>, inside: Rendered<NPSelection>,
} }
@ -702,6 +697,11 @@ export type Rendered<T extends NPSelection | EqCompSelection | AdjectiveSelectio
type: "subjectSelection", type: "subjectSelection",
selection: Rendered<NPSelection>, selection: Rendered<NPSelection>,
} }
: T extends ObjectSelectionComplete
? {
type: "objectSelection",
selection: Rendered<NPSelection> | Person.ThirdPlurMale | "none",
}
: ReplaceKey< : ReplaceKey<
Omit<T, "changeGender" | "changeNumber" | "changeDistance" | "adjectives" | "possesor">, Omit<T, "changeGender" | "changeNumber" | "changeDistance" | "adjectives" | "possesor">,
"e", "e",
@ -741,6 +741,16 @@ export type EPSBlockComplete = {
block: SubjectSelectionComplete | APSelection, block: SubjectSelectionComplete | APSelection,
}; };
export type VPSBlock = {
key: number,
// TODO: confusing use of APSelection / should be like APSelection s APSelection complete like the others
block: SubjectSelection | ObjectSelection | (APSelection | undefined),
};
export type VPSBlockComplete = {
key: number,
block: SubjectSelectionComplete | ObjectSelectionComplete | APSelection,
};
export type EPSelectionComplete = Omit<EPSelectionState, "predicate" | "blocks"> & { export type EPSelectionComplete = Omit<EPSelectionState, "predicate" | "blocks"> & {
blocks: EPSBlockComplete[], blocks: EPSBlockComplete[],
predicate: { predicate: {