APs and moveable blocks for verb phrases!
This commit is contained in:
parent
46fcc5cbb5
commit
9f82e6d085
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@lingdocs/pashto-inflector",
|
||||
"version": "2.5.9",
|
||||
"version": "2.6.0",
|
||||
"author": "lingdocs.com",
|
||||
"description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations",
|
||||
"homepage": "https://verbs.lingdocs.com",
|
||||
|
|
|
@ -195,11 +195,10 @@ function ConjugationViewer({ entry, complement, textOptions, showOnly, highlight
|
|||
try {
|
||||
return conjugateVerb(entry, complement);
|
||||
} catch(e) {
|
||||
console.log("conjugation error", e);
|
||||
console.error("conjugation error", e);
|
||||
return undefined;
|
||||
}
|
||||
})();
|
||||
console.log(conjugation);
|
||||
if (conjugation === undefined) {
|
||||
// don't show the conjugation viewer if the verb can't be conjugated
|
||||
return null;
|
||||
|
|
|
@ -40,7 +40,7 @@ function EPExplorer(props: {
|
|||
const parent = useRef<HTMLDivElement>(null);
|
||||
useEffect(() => {
|
||||
parent.current && autoAnimate(parent.current);
|
||||
}, [parent])
|
||||
}, [parent]);
|
||||
const subject = getSubjectSelection(eps.blocks).selection;
|
||||
const king = subject?.type === "pronoun"
|
||||
? "subject"
|
||||
|
@ -65,7 +65,7 @@ function EPExplorer(props: {
|
|||
handleChange={setMode}
|
||||
/>
|
||||
</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" }}>
|
||||
{mode === "phrases" && <>
|
||||
{eps.blocks.map(({ block, key }, i) => (
|
||||
|
|
|
@ -5,8 +5,7 @@ import {
|
|||
} from "../../lib/misc-helpers";
|
||||
import { isUnisexNounEntry } from "../../lib/type-predicates";
|
||||
import { checkForMiniPronounsError } from "../../lib/phrase-building/compile";
|
||||
import { adjustSubjectSelection, getSubjectSelection, makeAPBlock } from "../../lib/phrase-building/blocks-utils";
|
||||
import { assertNever } from "assert-never";
|
||||
import { adjustSubjectSelection, getSubjectSelection, insertNewAP, removeAP, setAP, shiftBlock } from "../../lib/phrase-building/blocks-utils";
|
||||
|
||||
type EpsReducerAction = {
|
||||
type: "set predicate type",
|
||||
|
@ -151,50 +150,30 @@ export default function epsReducer(eps: T.EPSelectionState, action: EpsReducerAc
|
|||
if (action.type === "insert new AP") {
|
||||
return {
|
||||
...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") {
|
||||
const blocks = [...eps.blocks];
|
||||
const { index, AP } = action.payload;
|
||||
blocks[index].block = AP;
|
||||
return {
|
||||
...eps,
|
||||
blocks,
|
||||
blocks: setAP(eps.blocks, index, AP),
|
||||
};
|
||||
}
|
||||
if (action.type === "remove AP") {
|
||||
const blocks = [...eps.blocks];
|
||||
blocks.splice(action.payload, 1);
|
||||
return {
|
||||
...eps,
|
||||
blocks,
|
||||
blocks: removeAP(eps.blocks, action.payload),
|
||||
};
|
||||
}
|
||||
if (action.type === "shift block") {
|
||||
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 {
|
||||
...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 {
|
||||
|
|
|
@ -58,7 +58,7 @@ function AdjectiveManager(props: {
|
|||
<div>Adjective</div>
|
||||
<div className="d-flex flex-row align-items-baseline">
|
||||
{!!props.adjectives.length && !adding && <div>
|
||||
<h6 onClick={() => setAdding(true)}>+ Adj.</h6>
|
||||
<h6 className="clickable" onClick={() => setAdding(true)}>+ Adj.</h6>
|
||||
</div>}
|
||||
<div onClick={deleteAdj(i)} className="ml-4">
|
||||
<div className="fas fa-trash" />
|
||||
|
@ -76,7 +76,7 @@ function AdjectiveManager(props: {
|
|||
/>
|
||||
</div>)}
|
||||
{!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>}
|
||||
</div>;
|
||||
}
|
||||
|
|
|
@ -46,9 +46,11 @@ function AdjectivePicker(props: {
|
|||
{!props.noTitle && <div>
|
||||
<h6>Adjective</h6>
|
||||
</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></div>}
|
||||
: <div></div>} */}
|
||||
<div />
|
||||
</div>
|
||||
<div className="mt-1">
|
||||
<EntrySelect
|
||||
|
|
|
@ -24,7 +24,13 @@ import {
|
|||
} from "../../lib/translate-phonetics";
|
||||
|
||||
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 ps = plainTextPsAdjustment(
|
||||
{ p: e.entry.p, f: removeFVarients(e.entry.f) },
|
||||
|
|
|
@ -1,37 +1,28 @@
|
|||
import { compileVP } from "../../lib/phrase-building/compile";
|
||||
import * as T from "../../types";
|
||||
import AbbreviationFormSelector from "./AbbreviationFormSelector";
|
||||
import useStickyState from "../../lib/useStickyState";
|
||||
import Examples from "../Examples";
|
||||
import { getObjectSelection, getSubjectSelection } from "../../lib/phrase-building/blocks-utils";
|
||||
|
||||
function VPDisplay({ VP, opts, setForm }: {
|
||||
VP: T.VPSelectionState | T.VPRendered,
|
||||
opts: T.TextOptions,
|
||||
setForm: (form: T.FormVersion) => void,
|
||||
}) {
|
||||
const [OSV, setOSV] = useStickyState<boolean>(false, "includeOSV");
|
||||
if (!("type" in VP)) {
|
||||
return <div className="lead text-muted text-center mt-4">
|
||||
{(() => {
|
||||
const twoNPs = (VP.subject === undefined) && (VP.object === undefined);
|
||||
return `Choose NP${twoNPs ? "s " : ""} to make a phrase`;
|
||||
const subject = getSubjectSelection(VP.blocks).selection;
|
||||
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>;
|
||||
}
|
||||
const result = compileVP(VP, { ...VP.form, OSV });
|
||||
const result = compileVP(VP, { ...VP.form });
|
||||
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
|
||||
adjustable={VP.whatsAdjustable}
|
||||
form={VP.form}
|
||||
|
|
|
@ -7,7 +7,7 @@ import * as T from "../../types";
|
|||
import ChartDisplay from "./VPChartDisplay";
|
||||
import useStickyState, { useStickyReducer } from "../../lib/useStickyState";
|
||||
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 { completeVPSelection, isPastTense } from "../../lib/phrase-building/vp-tools";
|
||||
import VPExplorerQuiz from "./VPExplorerQuiz";
|
||||
|
@ -16,6 +16,9 @@ import VPExplorerExplanationModal, { roleIcon } from "./VPExplorerExplanationMod
|
|||
import LZString from "lz-string";
|
||||
import { vpsReducer } from "./vps-reducer";
|
||||
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";
|
||||
|
||||
|
@ -41,7 +44,7 @@ function VPExplorer(props: {
|
|||
props.loaded
|
||||
? props.loaded
|
||||
: savedVps => makeVPSelectionState(props.verb, savedVps),
|
||||
"vpsState9",
|
||||
"vpsState12",
|
||||
flashMessage,
|
||||
);
|
||||
const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">(
|
||||
|
@ -55,6 +58,10 @@ function VPExplorer(props: {
|
|||
const [showShareClipped, setShowShareClipped] = useState<boolean>(false);
|
||||
const [alert, setAlert] = useState<string | undefined>(undefined);
|
||||
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 roles = getKingAndServant(
|
||||
isPast,
|
||||
|
@ -75,7 +82,6 @@ function VPExplorer(props: {
|
|||
}, [props.verb]);
|
||||
useEffect(() => {
|
||||
const VPSFromUrl = getVPSFromUrl();
|
||||
console.log({ VPSFromUrl });
|
||||
if (VPSFromUrl) {
|
||||
setMode("phrases");
|
||||
adjustVps({
|
||||
|
@ -124,6 +130,8 @@ function VPExplorer(props: {
|
|||
setShowShareClipped(false);
|
||||
}, 1250);
|
||||
}
|
||||
const object = getObjectSelection(vps.blocks).selection;
|
||||
const subject = getSubjectSelection(vps.blocks).selection;
|
||||
const VPS = completeVPSelection(vps);
|
||||
const phraseIsComplete = !!VPS;
|
||||
const rendered = VPS ? renderVP(VPS) : undefined;
|
||||
|
@ -160,54 +168,57 @@ function VPExplorer(props: {
|
|||
{mode === "phrases" ? <i className="fas fa-share-alt" /> : ""}
|
||||
</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">
|
||||
<button onClick={handleSubjObjSwap} className="btn btn-sm btn-light">
|
||||
<i className="fas fa-exchange-alt mr-2" /> subj/obj
|
||||
</button>
|
||||
</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" && <>
|
||||
<div className="my-2" style={{ background: (servantIsShrunk && roles.servant === "subject") ? shrunkenBackground : "inherit" }}>
|
||||
<NPPicker
|
||||
phraseIsComplete={phraseIsComplete}
|
||||
heading={roles.king === "subject"
|
||||
? <div className="h5 text-center" onClick={() => setShowingExplanation({ role: "king", item: "subject" })}>Subject {roleIcon.king}</div>
|
||||
: <div className="h5 text-center">
|
||||
Subject
|
||||
{` `}
|
||||
<span className="clickable" onClick={() => setShowingExplanation({ role: "servant", item: "subject" })}>{roleIcon.servant}</span>
|
||||
{` `}
|
||||
{(rendered && rendered.whatsAdjustable !== "king") &&
|
||||
<span onClick={toggleServantShrink} className="mx-2 clickable">
|
||||
{!servantIsShrunk ? "🪄" : "👶"}
|
||||
</span>
|
||||
}
|
||||
</div>}
|
||||
entryFeeder={props.entryFeeder}
|
||||
role={(isPast && vps.verb.transitivity !== "intransitive")
|
||||
? "ergative"
|
||||
: "subject"
|
||||
}
|
||||
is2ndPersonPicker={vps.verb.tenseCategory === "imperative"}
|
||||
np={vps.subject}
|
||||
counterPart={vps.verb ? vps.object : undefined}
|
||||
onChange={handleSubjectChange}
|
||||
opts={props.opts}
|
||||
isShrunk={(servantIsShrunk && roles.servant === "subject")}
|
||||
/>
|
||||
</div>
|
||||
{vps.verb && (vps.object !== "none") && <div className="my-2" style={{ background: (servantIsShrunk && roles.servant === "object") ? shrunkenBackground : "inherit" }}>
|
||||
{(typeof vps.object === "number")
|
||||
? <div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div>
|
||||
: <NPPicker
|
||||
phraseIsComplete={phraseIsComplete}
|
||||
heading={roles.king === "object"
|
||||
? <div className="h5 text-center clickable" onClick={() => setShowingExplanation({ role: "king", item: "object" })}>Object {roleIcon.king}</div>
|
||||
{vps.blocks.map(({ block, key }, i, blocks) => {
|
||||
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}
|
||||
heading={roles.king === "subject"
|
||||
? <div className="h5 text-center" onClick={() => setShowingExplanation({ role: "king", item: "subject" })}>Subject {roleIcon.king}</div>
|
||||
: <div className="h5 text-center">
|
||||
Object
|
||||
Subject
|
||||
{` `}
|
||||
<span className="clickable" onClick={() => setShowingExplanation({ role: "servant", item: "object" })}>{roleIcon.servant}</span>
|
||||
<span className="clickable" onClick={() => setShowingExplanation({ role: "servant", item: "subject" })}>{roleIcon.servant}</span>
|
||||
{` `}
|
||||
{(rendered && rendered.whatsAdjustable !== "king") &&
|
||||
<span onClick={toggleServantShrink} className="mx-2 clickable">
|
||||
|
@ -215,15 +226,48 @@ function VPExplorer(props: {
|
|||
</span>
|
||||
}
|
||||
</div>}
|
||||
entryFeeder={props.entryFeeder}
|
||||
role="object"
|
||||
np={vps.object}
|
||||
counterPart={vps.subject}
|
||||
onChange={handleObjectChange}
|
||||
opts={props.opts}
|
||||
isShrunk={(servantIsShrunk && roles.servant === "object")}
|
||||
/>}
|
||||
</div>}
|
||||
entryFeeder={props.entryFeeder}
|
||||
np={block.selection}
|
||||
counterPart={vps.verb ? object : undefined}
|
||||
role={(isPast && vps.verb.transitivity !== "intransitive")
|
||||
? "ergative"
|
||||
: "subject"
|
||||
}
|
||||
onChange={handleSubjectChange}
|
||||
opts={props.opts}
|
||||
isShrunk={(servantIsShrunk && roles.servant === "subject")}
|
||||
/>
|
||||
: (vps.verb && block?.type === "objectSelection" && block.selection !== "none")
|
||||
? <div className="my-2" style={{ background: (servantIsShrunk && roles.servant === "object") ? shrunkenBackground : "inherit" }}>
|
||||
{(typeof block.selection === "number")
|
||||
? <div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div>
|
||||
: <NPPicker
|
||||
phraseIsComplete={phraseIsComplete}
|
||||
heading={roles.king === "object"
|
||||
? <div className="h5 text-center clickable" onClick={() => setShowingExplanation({ role: "king", item: "object" })}>Object {roleIcon.king}</div>
|
||||
: <div className="h5 text-center">
|
||||
Object
|
||||
{` `}
|
||||
<span className="clickable" onClick={() => setShowingExplanation({ role: "servant", item: "object" })}>{roleIcon.servant}</span>
|
||||
{` `}
|
||||
{(rendered && rendered.whatsAdjustable !== "king") &&
|
||||
<span onClick={toggleServantShrink} className="mx-2 clickable">
|
||||
{!servantIsShrunk ? "🪄" : "👶"}
|
||||
</span>
|
||||
}
|
||||
</div>}
|
||||
entryFeeder={props.entryFeeder}
|
||||
role="object"
|
||||
np={block.selection}
|
||||
counterPart={subject}
|
||||
onChange={handleObjectChange}
|
||||
opts={props.opts}
|
||||
isShrunk={(servantIsShrunk && roles.servant === "object")}
|
||||
/>}
|
||||
</div>
|
||||
: null}
|
||||
</div>;
|
||||
})}
|
||||
</>}
|
||||
<div className="my-2">
|
||||
<TensePicker
|
||||
|
|
|
@ -18,6 +18,7 @@ import energyDrink from "./energy-drink.jpg";
|
|||
import { flattenLengths } from "../../lib/phrase-building/segment";
|
||||
import { concatPsString } from "../../lib/p-text-helpers";
|
||||
import { isImperativeTense } from "../../lib/type-predicates";
|
||||
import { adjustObjectSelection, adjustSubjectSelection, getObjectSelection, getRenderedObjectSelection, getRenderedSubjectSelection, getSubjectSelection } from "../../lib/phrase-building/blocks-utils";
|
||||
|
||||
const correctEmoji = ["✅", '🤓', "✅", '😊', "🌹", "✅", "✅", '🥳', "👏", "✅", "💯", "😎", "✅", "👍"];
|
||||
|
||||
|
@ -97,7 +98,8 @@ function VPExplorerQuiz(props: {
|
|||
}
|
||||
}
|
||||
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 });
|
||||
function handleRestart() {
|
||||
setWithBa(false);
|
||||
|
@ -177,9 +179,10 @@ function VPExplorerQuiz(props: {
|
|||
type="checkbox"
|
||||
checked={withBa}
|
||||
onChange={e => setWithBa(e.target.checked)}
|
||||
id="addBa"
|
||||
/>
|
||||
<label className="form-check-label text-muted" htmlFor="OSVCheckbox">
|
||||
add <InlinePs opts={props.opts}>{baParticle}</InlinePs> in phrase
|
||||
<label className="form-check-label text-muted" htmlFor="addBa">
|
||||
add <InlinePs opts={props.opts}>{baParticle}</InlinePs> in kids' section
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" className="btn btn-primary">
|
||||
|
@ -372,11 +375,13 @@ function getOptionFromResult(r: {
|
|||
}
|
||||
|
||||
function completeVPs(vps: T.VPSelectionState): T.VPSelectionComplete {
|
||||
const oldSubj = vps.subject?.type === "pronoun"
|
||||
? vps.subject.person
|
||||
const vpsSubj = getSubjectSelection(vps.blocks).selection;
|
||||
const vpsObj = getObjectSelection(vps.blocks).selection;
|
||||
const oldSubj = vpsSubj?.type === "pronoun"
|
||||
? vpsSubj.person
|
||||
: undefined;
|
||||
const oldObj = (typeof vps.object === "object" && vps.object.type === "pronoun")
|
||||
? vps.object.person
|
||||
const oldObj = (typeof vpsObj === "object" && vpsObj.type === "pronoun")
|
||||
? vpsObj.person
|
||||
: undefined;
|
||||
const { subj, obj } = randomSubjObj(
|
||||
oldSubj === undefined
|
||||
|
@ -393,29 +398,34 @@ function completeVPs(vps: T.VPSelectionState): T.VPSelectionComplete {
|
|||
};
|
||||
return {
|
||||
...vps,
|
||||
subject: {
|
||||
type: "pronoun",
|
||||
distance: "far",
|
||||
person: subj,
|
||||
},
|
||||
object: (
|
||||
(typeof vps.object === "object" && !(vps.object.type === "noun" && vps.object.dynamicComplement))
|
||||
||
|
||||
vps.object === undefined
|
||||
)
|
||||
? {
|
||||
blocks: adjustObjectSelection(
|
||||
adjustSubjectSelection(vps.blocks, {
|
||||
type: "pronoun",
|
||||
distance: "far",
|
||||
person: obj,
|
||||
}
|
||||
: vps.object,
|
||||
person: subj,
|
||||
}),
|
||||
(
|
||||
(typeof vpsObj === "object" && !(vpsObj.type === "noun" && vpsObj.dynamicComplement))
|
||||
||
|
||||
vpsObj === undefined
|
||||
)
|
||||
? {
|
||||
type: "pronoun",
|
||||
distance: "far",
|
||||
person: obj,
|
||||
}
|
||||
: vpsObj,
|
||||
),
|
||||
verb,
|
||||
};
|
||||
}
|
||||
|
||||
function getRandomVPSelection(mix: MixType = "both") {
|
||||
// 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")
|
||||
? subject.person
|
||||
: undefined;
|
||||
|
@ -444,27 +454,39 @@ function getRandomVPSelection(mix: MixType = "both") {
|
|||
// ensure that the verb selection is complete
|
||||
if (mix === "tenses") {
|
||||
return {
|
||||
subject: subject !== undefined ? subject : randSubj,
|
||||
object: object !== undefined ? object : randObj,
|
||||
blocks: possibleShuffleArray(adjustObjectSelection(
|
||||
adjustSubjectSelection(VPS.blocks, subject !== undefined ? subject : randSubj),
|
||||
object !== undefined ? object : randObj,
|
||||
)),
|
||||
verb: randomizeTense(verb, true),
|
||||
form: { removeKing: false, shrinkServant: false },
|
||||
}
|
||||
}
|
||||
return {
|
||||
subject: randSubj,
|
||||
object: (
|
||||
(typeof object === "object" && !(object.type === "noun" && object.dynamicComplement))
|
||||
||
|
||||
object === undefined
|
||||
)
|
||||
? randObj
|
||||
: object,
|
||||
blocks: possibleShuffleArray(adjustObjectSelection(
|
||||
adjustSubjectSelection(VPS.blocks, randSubj),
|
||||
(
|
||||
(typeof object === "object" && !(object.type === "noun" && object.dynamicComplement))
|
||||
||
|
||||
object === undefined
|
||||
)
|
||||
? randObj
|
||||
: object,
|
||||
)),
|
||||
verb: randomizeTense(verb, true),
|
||||
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 {
|
||||
return {
|
||||
...verb,
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
} from "../np-picker/picker-tools";
|
||||
import * as T from "../../types";
|
||||
import { getVerbInfo } from "../../lib/verb-info";
|
||||
import { adjustObjectSelection, getObjectSelection, getSubjectSelection, makeObjectSelection, makeSubjectSelection } from "../../lib/phrase-building/blocks-utils";
|
||||
|
||||
export function makeVPSelectionState(
|
||||
verb: T.VerbEntry,
|
||||
|
@ -11,16 +12,17 @@ export function makeVPSelectionState(
|
|||
const info = getVerbInfo(verb.entry, verb.complement);
|
||||
const subject = (os?.verb.voice === "passive" && info.type === "dynamic compound")
|
||||
? makeNounSelection(info.objComplement.entry as T.NounEntry, undefined, true)
|
||||
: (os?.subject || undefined);
|
||||
: (os?.blocks ? getSubjectSelection(os.blocks) : undefined);
|
||||
function getTransObjFromos() {
|
||||
const osObj = os ? getObjectSelection(os.blocks).selection : undefined;
|
||||
if (
|
||||
!os ||
|
||||
os.object === "none" ||
|
||||
typeof os.object === "number" ||
|
||||
osObj === "none" ||
|
||||
typeof osObj === "number" ||
|
||||
os.verb.isCompound === "dynamic" ||
|
||||
(os.object?.type === "noun" && os.object.dynamicComplement)
|
||||
(osObj?.type === "noun" && osObj.dynamicComplement)
|
||||
) return undefined;
|
||||
return os.object;
|
||||
return osObj;
|
||||
}
|
||||
const transitivity: T.Transitivity = "grammaticallyTransitive" in info
|
||||
? "transitive"
|
||||
|
@ -45,9 +47,12 @@ export function makeVPSelectionState(
|
|||
: "dynamic" in info
|
||||
? { entry: info.dynamic.auxVerb } as T.VerbEntry
|
||||
: undefined;
|
||||
const blocks = [
|
||||
{ key: Math.random(), block: makeSubjectSelection(subject) },
|
||||
{ key: Math.random(), block: makeObjectSelection(object) },
|
||||
];
|
||||
return {
|
||||
subject,
|
||||
object,
|
||||
blocks,
|
||||
verb: {
|
||||
type: "verb",
|
||||
verb: verb,
|
||||
|
@ -77,9 +82,12 @@ export function changeStatDyn(v: T.VPSelectionState, s: "dynamic" | "stative"):
|
|||
}
|
||||
return {
|
||||
...v,
|
||||
object: s === "dynamic"
|
||||
? makeNounSelection(info.dynamic.objComplement.entry as T.NounEntry, undefined, true)
|
||||
: undefined,
|
||||
blocks: adjustObjectSelection(
|
||||
v.blocks,
|
||||
s === "dynamic"
|
||||
? makeNounSelection(info.dynamic.objComplement.entry as T.NounEntry, undefined, true)
|
||||
: undefined,
|
||||
),
|
||||
verb: {
|
||||
...v.verb,
|
||||
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 {
|
||||
return {
|
||||
...v,
|
||||
object: transitivity === "grammatically transitive" ? T.Person.ThirdPlurMale : undefined,
|
||||
blocks: adjustObjectSelection(v.blocks, transitivity === "grammatically transitive" ? T.Person.ThirdPlurMale : undefined),
|
||||
verb: {
|
||||
...v.verb,
|
||||
transitivity,
|
||||
|
|
|
@ -13,6 +13,7 @@ import { checkForMiniPronounsError } from "../../lib/phrase-building/compile";
|
|||
import {
|
||||
makeVPSelectionState,
|
||||
} from "./verb-selection";
|
||||
import { adjustObjectSelection, adjustSubjectSelection, getObjectSelection, getSubjectSelection, insertNewAP, removeAP, setAP, shiftBlock } from "../../lib/phrase-building/blocks-utils";
|
||||
|
||||
export type VpsReducerAction = {
|
||||
type: "load vps",
|
||||
|
@ -54,7 +55,24 @@ export type VpsReducerAction = {
|
|||
} | {
|
||||
type: "set verb",
|
||||
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 {
|
||||
return ensureMiniPronounsOk(vps, doReduce());
|
||||
|
@ -68,38 +86,40 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
|
|||
}
|
||||
function doReduce(): T.VPSelectionState {
|
||||
if (action.type === "load vps") {
|
||||
console.log("doing loading", action.payload);
|
||||
return action.payload;
|
||||
}
|
||||
if (action.type === "set subject") {
|
||||
const { subject, skipPronounConflictCheck } = action.payload;
|
||||
const object = getObjectSelection(vps.blocks).selection;
|
||||
if (
|
||||
!skipPronounConflictCheck
|
||||
&&
|
||||
hasPronounConflict(subject, vps.object)
|
||||
hasPronounConflict(subject, object)
|
||||
) {
|
||||
if (sendAlert) sendAlert("That combination of pronouns is not allowed");
|
||||
return vps;
|
||||
}
|
||||
return {
|
||||
...vps,
|
||||
subject: action.payload.subject,
|
||||
blocks: adjustSubjectSelection(vps.blocks, action.payload.subject),
|
||||
};
|
||||
}
|
||||
if (action.type === "set object") {
|
||||
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;
|
||||
}
|
||||
const object = action.payload;
|
||||
// check for pronoun conflict
|
||||
if (hasPronounConflict(vps.subject, object)) {
|
||||
if (hasPronounConflict(subjectB, object)) {
|
||||
if (sendAlert) sendAlert("That combination of pronouns is not allowed");
|
||||
return vps;
|
||||
}
|
||||
return {
|
||||
...vps,
|
||||
object,
|
||||
blocks: adjustObjectSelection(vps.blocks, object),
|
||||
};
|
||||
}
|
||||
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 (vps.verb && vps.verb.canChangeVoice) {
|
||||
const subject = getSubjectSelection(vps.blocks).selection;
|
||||
const object = getObjectSelection(vps.blocks).selection;
|
||||
const voice = action.payload;
|
||||
if (voice === "passive" && vps.verb.tenseCategory === "imperative") {
|
||||
return vps;
|
||||
|
@ -121,8 +143,10 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
|
|||
if (voice === "passive") {
|
||||
return {
|
||||
...vps,
|
||||
subject: typeof vps.object === "object" ? vps.object : undefined,
|
||||
object: "none",
|
||||
blocks: adjustObjectSelection(
|
||||
adjustSubjectSelection(vps.blocks, typeof object === "object" ? object : undefined),
|
||||
"none",
|
||||
),
|
||||
verb: {
|
||||
...vps.verb,
|
||||
voice,
|
||||
|
@ -131,8 +155,10 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
|
|||
} else {
|
||||
return {
|
||||
...vps,
|
||||
subject: undefined,
|
||||
object: typeof vps.subject === "object" ? vps.subject : undefined,
|
||||
blocks: adjustObjectSelection(
|
||||
adjustSubjectSelection(vps.blocks, undefined),
|
||||
typeof subject === "object" ? subject : undefined,
|
||||
),
|
||||
verb: {
|
||||
...vps.verb,
|
||||
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);
|
||||
// }
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,8 +54,8 @@ export function randomSubjObj(old?: { subj: T.Person, obj?: T.Person }): { subj:
|
|||
}
|
||||
export function getEnglishVerb(entry: T.DictionaryEntry): string {
|
||||
if (!entry.ec) {
|
||||
console.log("errored verb");
|
||||
console.log(entry);
|
||||
console.error("errored verb");
|
||||
console.error(entry);
|
||||
throw new Error("no english information for verb");
|
||||
}
|
||||
if (entry.ep) {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import * as T from "../../types";
|
||||
|
||||
export function getSubjectSelection(blocks: T.EPSBlockComplete[]): T.SubjectSelectionComplete;
|
||||
export function getSubjectSelection(blocks: T.EPSBlock[]): T.SubjectSelection;
|
||||
export function getSubjectSelection(blocks: T.EPSBlock[] | T.EPSBlockComplete[]): T.SubjectSelection | T.SubjectSelectionComplete {
|
||||
export function getSubjectSelection(blocks: T.EPSBlockComplete[] | T.VPSBlockComplete[]): T.SubjectSelectionComplete;
|
||||
export function getSubjectSelection(blocks: T.EPSBlock[] | T.VPSBlock[]): T.SubjectSelection;
|
||||
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");
|
||||
if (!b || !b.block || b.block.type !== "subjectSelection") {
|
||||
throw new Error("subjectSelection not found in blocks");
|
||||
|
@ -10,14 +10,33 @@ export function getSubjectSelection(blocks: T.EPSBlock[] | T.EPSBlockComplete[])
|
|||
return b.block;
|
||||
}
|
||||
|
||||
export function getRenderedSubjectSelection(blocks: (T.Rendered<T.SubjectSelectionComplete> | T.Rendered<T.APSelection>)[]): T.Rendered<T.SubjectSelectionComplete> {
|
||||
const b = blocks.find(f => f.type === "subjectSelection");
|
||||
if (!b || b.type !== "subjectSelection") {
|
||||
export function getObjectSelection(blocks: T.VPSBlockComplete[]): T.ObjectSelectionComplete;
|
||||
export function getObjectSelection(blocks: T.VPSBlock[]): T.ObjectSelection;
|
||||
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");
|
||||
}
|
||||
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[] {
|
||||
return [
|
||||
{
|
||||
|
@ -30,20 +49,33 @@ export function makeEPSBlocks(): T.EPSBlock[] {
|
|||
];
|
||||
}
|
||||
|
||||
export function makeAPBlock(): T.EPSBlock {
|
||||
export function makeAPBlock(): { key: number, block: undefined } {
|
||||
return {
|
||||
key: Math.random(),
|
||||
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 {
|
||||
type: "subjectSelection",
|
||||
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[] {
|
||||
if (blocks.some(block => block.block === undefined)) {
|
||||
return false;
|
||||
|
@ -52,9 +84,20 @@ export function EPSBlocksAreComplete(blocks: T.EPSBlock[]): blocks is T.EPSBlock
|
|||
return !!subject.selection;
|
||||
}
|
||||
|
||||
export function adjustSubjectSelection(blocks: T.EPSBlock[], subject: T.SubjectSelectionComplete | T.NPSelection): T.EPSBlockComplete[];
|
||||
export function adjustSubjectSelection(blocks: T.EPSBlock[], subject: T.SubjectSelection | undefined): T.EPSBlock[];
|
||||
export function adjustSubjectSelection(blocks: T.EPSBlock[], subject: T.SubjectSelection | T.SubjectSelectionComplete | T.NPSelection | undefined): T.EPSBlock[] | T.EPSBlockComplete[] {
|
||||
export function VPSBlocksAreComplete(blocks: T.VPSBlock[]): blocks is T.VPSBlockComplete[] {
|
||||
if (blocks.some(block => block.block === undefined)) {
|
||||
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 i = nb.findIndex(b => b.block && b.block.type === "subjectSelection");
|
||||
if (i === -1) {
|
||||
|
@ -62,4 +105,57 @@ export function adjustSubjectSelection(blocks: T.EPSBlock[], subject: T.SubjectS
|
|||
}
|
||||
nb[i].block = subject?.type === "subjectSelection" ? subject : makeSubjectSelection(subject);
|
||||
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;
|
||||
};
|
|
@ -25,7 +25,7 @@ import { pronouns } from "../grammar-units";
|
|||
import { completeEPSelection, renderEP } from "./render-ep";
|
||||
import { completeVPSelection } from "./vp-tools";
|
||||
import { renderVP } from "./render-vp";
|
||||
import { getRenderedSubjectSelection } from "./blocks-utils";
|
||||
import { getRenderedObjectSelection, getRenderedSubjectSelection } from "./blocks-utils";
|
||||
|
||||
const blank: T.PsString = {
|
||||
p: "______",
|
||||
|
@ -35,14 +35,13 @@ type BlankoutOptions = { equative?: boolean, ba?: boolean, kidsSection?: boolean
|
|||
|
||||
const kidsBlank = makeSegment({ p: "___", f: "___" }, ["isKid"]);
|
||||
|
||||
type Form = T.FormVersion & { OSV?: boolean };
|
||||
export function compileVP(VP: T.VPRendered, form: Form): { ps: T.SingleOrLengthOpts<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: Form, combineLengths?: true): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] } {
|
||||
export function compileVP(VP: T.VPRendered, form: T.FormVersion): { 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: T.FormVersion, combineLengths?: true): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] } {
|
||||
const verb = VP.verb.ps;
|
||||
const { kids, NPs } = getVPSegmentsAndKids(VP, form);
|
||||
const { kids, blocks } = getVPSegmentsAndKids(VP, form);
|
||||
const psResult = compilePs({
|
||||
NPs,
|
||||
blocks,
|
||||
kids,
|
||||
verb,
|
||||
VP,
|
||||
|
@ -50,12 +49,12 @@ export function compileVP(VP: T.VPRendered, form: Form, combineLengths?: true):
|
|||
return {
|
||||
ps: combineLengths ? flattenLengths(psResult) : psResult,
|
||||
// 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 = {
|
||||
NPs: Segment[][],
|
||||
blocks: Segment[],
|
||||
kids: Segment[],
|
||||
verb: {
|
||||
head: T.PsString | undefined,
|
||||
|
@ -64,13 +63,13 @@ type CompilePsInput = {
|
|||
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) {
|
||||
return {
|
||||
long: compilePs({ NPs, verb: { head, rest: rest.long }, VP, kids }) as T.PsString[],
|
||||
short: compilePs({ NPs, verb: { head, rest: rest.short }, VP, kids }) as T.PsString[],
|
||||
long: compilePs({ blocks, verb: { head, rest: rest.long }, VP, kids }) as T.PsString[],
|
||||
short: compilePs({ blocks, verb: { head, rest: rest.short }, VP, kids }) as T.PsString[],
|
||||
...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:
|
||||
// a. potential different versions of where the nu goes
|
||||
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
|
||||
// 1. put in kids in the kids section
|
||||
const segments = putKidsInKidsSection([...NP, ...verbSegments], kids);
|
||||
// 2. space out the words properly
|
||||
const withProperSpaces = addSpacesBetweenSegments(segments);
|
||||
// 3. throw it all together into a PsString for each permutation
|
||||
return combineSegments(withProperSpaces);
|
||||
})
|
||||
)));
|
||||
return removeDuplicates(verbWNegativeVersions.flatMap((verbSegments) => {
|
||||
// for each permutation of the possible ordering of NPs and Verb + nu
|
||||
// 1. put in kids in the kids section
|
||||
const segments = putKidsInKidsSection([...blocks, ...verbSegments], kids);
|
||||
// 2. space out the words properly
|
||||
const withProperSpaces = addSpacesBetweenSegments(segments);
|
||||
// 3. throw it all together into a PsString for each permutation
|
||||
return combineSegments(withProperSpaces);
|
||||
}));
|
||||
}
|
||||
|
||||
export function getShrunkenServant(VP: T.VPRendered): Segment | undefined {
|
||||
const shrinkServant = VP.form.shrinkServant && !(VP.isCompound === "dynamic" && !VP.isPast);
|
||||
const toShrinkServant = (() => {
|
||||
const toShrinkServant: T.Rendered<T.NPSelection> | undefined = (() => {
|
||||
if (!shrinkServant) 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;
|
||||
return servant;
|
||||
})();
|
||||
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 shrunkenServant = getShrunkenServant(VP);
|
||||
const possToShrink = findPossesivesToShrinkInVP(VP, {
|
||||
shrunkServant: !!shrunkenServant,
|
||||
removedKing: removeKing,
|
||||
});
|
||||
const SO = {
|
||||
subject: getPashtoFromRendered(VP.subject, false),
|
||||
object: typeof VP.object === "object" ? getPashtoFromRendered(VP.object, VP.subject.person) : undefined,
|
||||
};
|
||||
function getSegment(t: "subject" | "object"): Segment | undefined {
|
||||
const word = (VP.servant === t)
|
||||
? (!shrunkenServant ? SO[t] : undefined)
|
||||
: (VP.king === t)
|
||||
? (!removeKing ? SO[t] : undefined)
|
||||
: undefined;
|
||||
if (!word) return undefined;
|
||||
return makeSegment(word);
|
||||
}
|
||||
const subject = getSegment("subject");
|
||||
const object = getSegment("object");
|
||||
|
||||
const subject = getRenderedSubjectSelection(VP.blocks).selection;
|
||||
const blocks = VP.blocks.reduce((accum, block) => {
|
||||
if (block.type === "subjectSelection") {
|
||||
if (VP.servant === "subject" && shrunkenServant) return accum;
|
||||
if (VP.king === "subject" && removeKing) return accum;
|
||||
return [
|
||||
...accum,
|
||||
makeSegment(getPashtoFromRendered(block.selection, false)),
|
||||
];
|
||||
}
|
||||
if (block.type === "objectSelection") {
|
||||
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 {
|
||||
kids: orderKidsSection([
|
||||
...VP.verb.hasBa
|
||||
|
@ -135,14 +141,37 @@ export function getVPSegmentsAndKids(VP: T.VPRendered, form?: Form): { kids: Seg
|
|||
? [shrunkenServant] : [],
|
||||
...possToShrink.map(shrinkNP),
|
||||
]),
|
||||
NPs: [
|
||||
[
|
||||
...subject ? [subject] : [],
|
||||
...object ? [object] : [],
|
||||
],
|
||||
// TODO: make this an option to also include O S V order ??
|
||||
// also show O S V if both are showing
|
||||
...(subject && object && (form && form.OSV)) ? [[object, subject]] : [],
|
||||
blocks: blocks,
|
||||
};
|
||||
}
|
||||
|
||||
export function getEPSegmentsAndKids(EP: T.EPRendered, blankOut?: BlankoutOptions): { kids: Segment[], blocks: Segment[] } {
|
||||
const possToShrink = findPossesivesToShrinkInEP(EP);
|
||||
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)),
|
||||
]),
|
||||
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, 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[] } {
|
||||
const { kids, NPs } = getEPSegmentsAndKids(EP, blankOut);
|
||||
const { kids, blocks } = getEPSegmentsAndKids(EP, blankOut);
|
||||
const equative = EP.equative.ps;
|
||||
const psResult = compileEPPs({
|
||||
NPs,
|
||||
blocks,
|
||||
kids,
|
||||
equative: blankOut?.equative ? [blank] : equative,
|
||||
negative: EP.equative.negative,
|
||||
});
|
||||
return {
|
||||
ps: combineLengths ? flattenLengths(psResult) : psResult,
|
||||
e: compileEPEnglish(EP),
|
||||
e: compileEnglishEP(EP),
|
||||
};
|
||||
}
|
||||
|
||||
export function getEPSegmentsAndKids(EP: T.EPRendered, blankOut?: BlankoutOptions): { kids: Segment[], NPs: Segment[] } {
|
||||
const possToShrink = findPossesivesToShrinkInEP(EP);
|
||||
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[],
|
||||
function compileEPPs({ blocks, kids, equative, negative }: {
|
||||
blocks: Segment[],
|
||||
kids: Segment[],
|
||||
equative: T.SingleOrLengthOpts<T.PsString[]>,
|
||||
negative: boolean,
|
||||
}): T.SingleOrLengthOpts<T.PsString[]> {
|
||||
if ("long" in equative) {
|
||||
return {
|
||||
long: compileEPPs({ NPs, kids, equative: equative.long, negative }) as T.PsString[],
|
||||
short: compileEPPs({ NPs, kids, equative: equative.short, negative }) as T.PsString[],
|
||||
long: compileEPPs({ blocks, kids, equative: equative.long, negative }) as T.PsString[],
|
||||
short: compileEPPs({ blocks, kids, equative: equative.short, negative }) as T.PsString[],
|
||||
};
|
||||
}
|
||||
const allSegments = putKidsInKidsSection([
|
||||
...NPs,
|
||||
...blocks,
|
||||
...negative ? [
|
||||
makeSegment({ p: "نه", f: "nú" }),
|
||||
makeSegment(removeAccents(equative))
|
||||
|
@ -332,25 +330,7 @@ function compileEPPs({ NPs, kids, equative, negative }: {
|
|||
return removeDuplicates(combineSegments(allSegments, "spaces"));
|
||||
}
|
||||
|
||||
function compileEPEnglish(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;
|
||||
}
|
||||
|
||||
function getEnglishAPs(blocks: (T.Rendered<T.SubjectSelectionComplete> | T.Rendered<T.APSelection>)[]) {
|
||||
function getEnglishAPs(blocks: (T.Rendered<T.SubjectSelectionComplete> | T.Rendered<T.APSelection> | T.RenderedVPSBlock)[]) {
|
||||
const APs = blocks.filter(x => x.type !== "subjectSelection") as T.Rendered<T.APSelection>[];
|
||||
return APs.reduce((accum, curr) => {
|
||||
const e = getEnglishFromRendered(curr);
|
||||
|
@ -398,23 +378,42 @@ function addSpacesBetweenSegments(segments: Segment[]): (Segment | " " | "" | T.
|
|||
return o;
|
||||
}
|
||||
|
||||
function compileEnglish(VP: T.VPRendered): string[] | undefined {
|
||||
function insertEWords(e: string, { subject, object }: { subject: string, object?: string }): string {
|
||||
return e.replace("$SUBJ", subject).replace("$OBJ", object || "");
|
||||
function compileEnglishVP(VP: T.VPRendered): string[] | undefined {
|
||||
function insertEWords(e: string, { subject, object, APs }: { subject: string, object?: string, APs: string }): string {
|
||||
return e.replace("$SUBJ", subject).replace("$OBJ", object || "") + APs;
|
||||
}
|
||||
const engSubj = getEnglishFromRendered(VP.subject);
|
||||
const engObj = typeof VP.object === "object"
|
||||
? getEnglishFromRendered(VP.object)
|
||||
: undefined;
|
||||
const engSubj = getRenderedSubjectSelection(VP.blocks).selection;
|
||||
const obj = getRenderedObjectSelection(VP.blocks).selection;
|
||||
const engObj = typeof obj === "object" ? obj.e : "";
|
||||
const engAPs = getEnglishAPs(VP.blocks);
|
||||
// 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, {
|
||||
subject: engSubj,
|
||||
object: engObj,
|
||||
// TODO: make sure we actually have the english
|
||||
subject: getEnglishFromRendered(engSubj) || "",
|
||||
object: getEnglishFromRendered(engSubj) || "",
|
||||
APs: engAPs,
|
||||
}))
|
||||
: 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[] {
|
||||
const sorted = [...kids];
|
||||
|
@ -472,19 +471,34 @@ export function findPossesivesToShrinkInVP(VP: T.VPRendered, f: {
|
|||
shrunkServant: boolean,
|
||||
removedKing: boolean,
|
||||
}): T.Rendered<T.NPSelection>[] {
|
||||
return findPossesives(VP.subject, VP.object).filter(x => (
|
||||
// only give the possesive to shrink if it's not alread in a shrunken servant
|
||||
!(f.shrunkServant && x.role === "servant")
|
||||
&& // or in a removed king
|
||||
!(f.removedKing && x.role === "king")
|
||||
));
|
||||
}
|
||||
|
||||
function findPossesives(...nps: (T.Rendered<T.NPSelection> | T.ObjectNP | undefined)[]): T.Rendered<T.NPSelection>[] {
|
||||
return nps.reduce((accum, curr) => {
|
||||
const res = findPossesivesInNP(curr);
|
||||
if (res) return [...accum, ...res];
|
||||
return accum;
|
||||
return VP.blocks.reduce((found, block) => {
|
||||
if (block.type === "subjectSelection") {
|
||||
if (block.selection.role === "king" && f.removedKing) return found;
|
||||
if (block.selection.role === "servant" && f.shrunkServant) return found;
|
||||
return [
|
||||
...findPossesivesInNP(block.selection),
|
||||
...found,
|
||||
];
|
||||
}
|
||||
if (block.type === "objectSelection") {
|
||||
if (typeof block.selection !== "object") return found;
|
||||
if (block.selection.role === "king" && f.removedKing) return found;
|
||||
if (block.selection.role === "servant" && f.shrunkServant) return found;
|
||||
return [
|
||||
...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>[]);
|
||||
}
|
||||
|
||||
|
|
|
@ -206,7 +206,6 @@ export function getEnglishFromRendered(r: T.Rendered<T.NPSelection | T.EqCompSel
|
|||
const np = r as T.Rendered<T.NounSelection>;
|
||||
return addPossesors(np.possesor?.np, addArticlesAndAdjs(np), r.type);
|
||||
}
|
||||
// TODO: possesives in English for participles and pronouns too!
|
||||
return r.e;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
if (!e || typeof e !== "string") {
|
||||
console.log(e);
|
||||
throw new Error("error getting english for compliment");
|
||||
}
|
||||
return {
|
||||
|
@ -104,7 +103,6 @@ function renderEqCompSelection(s: T.EqCompSelection, person: T.Person): T.Render
|
|||
}
|
||||
const e = getEnglishWord(s.entry);
|
||||
if (!e || typeof e !== "string") {
|
||||
console.log(e);
|
||||
throw new Error("error getting english for compliment");
|
||||
}
|
||||
if (isLocativeAdverbEntry(s.entry)) {
|
||||
|
|
|
@ -22,26 +22,31 @@ import {
|
|||
import { renderEnglishVPBase } from "./english-vp-rendering";
|
||||
import { personGender } from "../../lib/misc-helpers";
|
||||
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
|
||||
|
||||
export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
|
||||
const subject = getSubjectSelection(VP.blocks).selection;
|
||||
const object = getObjectSelection(VP.blocks).selection;
|
||||
// Sentence Rules Logic
|
||||
const isPast = isPastTense(VP.verb.tense);
|
||||
const isTransitive = VP.object !== "none";
|
||||
const isTransitive = object !== "none";
|
||||
const { king, servant } = getKingAndServant(isPast, isTransitive);
|
||||
const kingPerson = getPersonFromNP(
|
||||
king === "subject" ? VP.subject : VP.object,
|
||||
king === "subject" ? subject : object,
|
||||
);
|
||||
// TODO: more elegant way of handling this type safety
|
||||
if (kingPerson === undefined) {
|
||||
throw new Error("king of sentance does not exist");
|
||||
}
|
||||
const subjectPerson = getPersonFromNP(VP.subject);
|
||||
const objectPerson = getPersonFromNP(VP.object);
|
||||
const subjectPerson = getPersonFromNP(subject);
|
||||
const objectPerson = getPersonFromNP(object);
|
||||
// TODO: also don't inflect if it's a pattern one animate noun
|
||||
const inflectSubject = isPast && isTransitive && !isMascSingAnimatePattern4(VP.subject);
|
||||
const inflectObject = !isPast && isFirstOrSecondPersPronoun(VP.object);
|
||||
const inflectSubject = isPast && isTransitive && !isMascSingAnimatePattern4(subject);
|
||||
const inflectObject = !isPast && isFirstOrSecondPersPronoun(object);
|
||||
// Render Elements
|
||||
const b: T.VPRendered = {
|
||||
type: "VPRendered",
|
||||
|
@ -50,12 +55,15 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
|
|||
isPast,
|
||||
isTransitive,
|
||||
isCompound: VP.verb.isCompound,
|
||||
subject: renderNPSelection(VP.subject, inflectSubject, false, "subject", king === "subject" ? "king" : "servant"),
|
||||
object: renderNPSelection(VP.object, inflectObject, true, "object", king === "object" ? "king" : "servant"),
|
||||
blocks: renderVPBlocks(VP.blocks, {
|
||||
inflectSubject,
|
||||
inflectObject,
|
||||
king,
|
||||
}),
|
||||
verb: renderVerbSelection(VP.verb, kingPerson, objectPerson),
|
||||
englishBase: renderEnglishVPBase({
|
||||
subjectPerson,
|
||||
object: VP.verb.isCompound === "dynamic" ? "none" : VP.object,
|
||||
object: VP.verb.isCompound === "dynamic" ? "none" : object,
|
||||
vs: VP.verb,
|
||||
}),
|
||||
form: VP.form,
|
||||
|
@ -64,6 +72,35 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
|
|||
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" {
|
||||
// TODO: intransitive dynamic compounds?
|
||||
return (VP.verb.isCompound === "dynamic" && VP.verb.transitivity === "transitive")
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
import { isImperativeTense, isPerfectTense } from "../type-predicates";
|
||||
import * as grammarUnits from "../../lib/grammar-units";
|
||||
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 {
|
||||
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(vps: T.VPSelectionComplete): T.VPSelectionComplete;
|
||||
export function switchSubjObj(vps: T.VPSelectionState | T.VPSelectionComplete): T.VPSelectionState | T.VPSelectionComplete {
|
||||
export function switchSubjObj<V extends T.VPSelectionState | T.VPSelectionComplete>(vps: V): V {
|
||||
const subject = getSubjectSelection(vps.blocks).selection;
|
||||
const object = getObjectSelection(vps.blocks).selection;
|
||||
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,
|
||||
subject: vps.object,
|
||||
object: vps.subject,
|
||||
blocks: adjustObjectSelection(
|
||||
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,
|
||||
subject: vps.object,
|
||||
object: vps.subject,
|
||||
blocks: adjustObjectSelection(
|
||||
adjustSubjectSelection(vps.blocks, object),
|
||||
subject,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
export function completeVPSelection(vps: T.VPSelectionState): T.VPSelectionComplete | undefined {
|
||||
if (vps.subject === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (vps.object === undefined) {
|
||||
if (!VPSBlocksAreComplete(vps.blocks)) {
|
||||
return undefined;
|
||||
}
|
||||
// necessary for this version on typscript ...
|
||||
|
@ -228,13 +230,10 @@ export function completeVPSelection(vps: T.VPSelectionState): T.VPSelectionCompl
|
|||
...vps.verb,
|
||||
tense: getTenseFromVerbSelection(vps.verb),
|
||||
};
|
||||
const subject = vps.subject;
|
||||
const object = vps.object
|
||||
return {
|
||||
...vps,
|
||||
subject,
|
||||
object,
|
||||
verb,
|
||||
blocks: vps.blocks,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -257,12 +256,12 @@ export function isThirdPerson(p: T.Person): boolean {
|
|||
}
|
||||
|
||||
export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState): T.VPSelectionState {
|
||||
console.log("checking more...", vps);
|
||||
const subjIs2ndPerson = (vps.subject?.type === "pronoun") && isSecondPerson(vps.subject.person);
|
||||
const objIs2ndPerson = (typeof vps.object === "object")
|
||||
&& (vps.object.type === "pronoun")
|
||||
&& isSecondPerson(vps.object.person);
|
||||
console.log({ subjIs2ndPerson, objIs2ndPerson });
|
||||
const subject = getSubjectSelection(vps.blocks).selection;
|
||||
const object = getObjectSelection(vps.blocks).selection;
|
||||
const subjIs2ndPerson = (subject?.type === "pronoun") && isSecondPerson(subject.person);
|
||||
const objIs2ndPerson = (typeof object === "object")
|
||||
&& (object.type === "pronoun")
|
||||
&& isSecondPerson(object.person);
|
||||
const default2ndPersSubject: T.PronounSelection = {
|
||||
type: "pronoun",
|
||||
distance: "far",
|
||||
|
@ -279,41 +278,39 @@ export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState):
|
|||
return vps;
|
||||
}
|
||||
if (subjIs2ndPerson && objIs2ndPerson) {
|
||||
if (typeof vps.object !== "object" || vps.object.type !== "pronoun") {
|
||||
if (typeof object !== "object" || object.type !== "pronoun") {
|
||||
return vps;
|
||||
}
|
||||
return {
|
||||
...vps,
|
||||
object: {
|
||||
...vps.object,
|
||||
blocks: adjustObjectSelection(vps.blocks, {
|
||||
...object,
|
||||
person: getNon2ndPersPronoun(),
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
if (!subjIs2ndPerson && objIs2ndPerson) {
|
||||
if (typeof vps.object !== "object" || vps.object.type !== "pronoun") {
|
||||
if (typeof object !== "object" || object.type !== "pronoun") {
|
||||
return {
|
||||
...vps,
|
||||
subject: default2ndPersSubject,
|
||||
blocks: adjustSubjectSelection(vps.blocks, default2ndPersSubject),
|
||||
};
|
||||
}
|
||||
return {
|
||||
...vps,
|
||||
subject: default2ndPersSubject,
|
||||
object: {
|
||||
...vps.object,
|
||||
person: getNon2ndPersPronoun(),
|
||||
},
|
||||
blocks: adjustObjectSelection(
|
||||
adjustSubjectSelection(vps.blocks, default2ndPersSubject),
|
||||
{
|
||||
...object,
|
||||
person: getNon2ndPersPronoun(),
|
||||
},
|
||||
),
|
||||
};
|
||||
}
|
||||
if (!subjIs2ndPerson && !objIs2ndPerson) {
|
||||
console.log("returning last");
|
||||
return {
|
||||
...vps,
|
||||
subject: default2ndPersSubject,
|
||||
verb: {
|
||||
...vps.verb,
|
||||
},
|
||||
blocks: adjustSubjectSelection(vps.blocks, default2ndPersSubject),
|
||||
};
|
||||
}
|
||||
throw new Error("error ensuring compatible VPSelection for imperative verb");
|
||||
|
|
|
@ -98,6 +98,12 @@ export const sandwiches: T.Sandwich[] = [
|
|||
after: { p: "په حیث", f: "pu heys" },
|
||||
e: "as",
|
||||
},
|
||||
{
|
||||
type: "sandwich",
|
||||
before: { p: "د", f: "du" },
|
||||
after: { p: "په لور", f: "pu lor" },
|
||||
e: "towards",
|
||||
},
|
||||
];
|
||||
|
||||
export default sandwiches;
|
File diff suppressed because one or more lines are too long
30
src/types.ts
30
src/types.ts
|
@ -497,7 +497,7 @@ export type Pattern6FemEntry<T extends FemNounEntry> = T & { __brand3: "non anim
|
|||
export type NonInflecting<T> = T & { __brand3: "non-inflecting" };
|
||||
|
||||
export type Entry = NounEntry | AdjectiveEntry | AdverbEntry | VerbEntry;
|
||||
|
||||
export type RenderedVPSBlock = (Rendered<SubjectSelectionComplete> | Rendered<ObjectSelectionComplete> | Rendered<APSelection>);
|
||||
// TODO: make this Rendered<VPSelectionComplete> with recursive Rendered<>
|
||||
export type VPRendered = {
|
||||
type: "VPRendered",
|
||||
|
@ -506,8 +506,7 @@ export type VPRendered = {
|
|||
isPast: boolean,
|
||||
isTransitive: boolean,
|
||||
isCompound: "stative" | "dynamic" | false,
|
||||
subject: Rendered<NPSelection>,
|
||||
object: Rendered<NPSelection> | ObjectNP,
|
||||
blocks: RenderedVPSBlock[],
|
||||
verb: VerbRendered,
|
||||
englishBase?: string[],
|
||||
form: FormVersion,
|
||||
|
@ -551,17 +550,13 @@ export type ObjectSelectionComplete = {
|
|||
};
|
||||
|
||||
export type VPSelectionState = {
|
||||
// blocks: (SubjectSelection | ObjectSelection)[]
|
||||
subject: NPSelection | undefined,
|
||||
object: NPSelection | ObjectNP | undefined,
|
||||
blocks: VPSBlock[]
|
||||
verb: VerbSelection,
|
||||
form: FormVersion,
|
||||
};
|
||||
|
||||
export type VPSelectionComplete = {
|
||||
// blocks: (SubjectSelectionComplete | ObjectSelectionComplete)[]
|
||||
subject: NPSelection,
|
||||
object: NPSelection | ObjectNP,
|
||||
blocks: VPSBlockComplete[]
|
||||
verb: VerbSelectionComplete,
|
||||
form: FormVersion,
|
||||
};
|
||||
|
@ -676,7 +671,7 @@ export type RenderedPossesorSelection = {
|
|||
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"> & {
|
||||
inside: Rendered<NPSelection>,
|
||||
}
|
||||
|
@ -702,6 +697,11 @@ export type Rendered<T extends NPSelection | EqCompSelection | AdjectiveSelectio
|
|||
type: "subjectSelection",
|
||||
selection: Rendered<NPSelection>,
|
||||
}
|
||||
: T extends ObjectSelectionComplete
|
||||
? {
|
||||
type: "objectSelection",
|
||||
selection: Rendered<NPSelection> | Person.ThirdPlurMale | "none",
|
||||
}
|
||||
: ReplaceKey<
|
||||
Omit<T, "changeGender" | "changeNumber" | "changeDistance" | "adjectives" | "possesor">,
|
||||
"e",
|
||||
|
@ -741,6 +741,16 @@ export type EPSBlockComplete = {
|
|||
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"> & {
|
||||
blocks: EPSBlockComplete[],
|
||||
predicate: {
|
||||
|
|
Loading…
Reference in New Issue