APs and moveable blocks for verb phrases!
This commit is contained in:
parent
46fcc5cbb5
commit
9f82e6d085
|
@ -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",
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) => (
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) },
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
|
@ -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>[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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
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 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: {
|
||||||
|
|
Loading…
Reference in New Issue