better format with NP and AP selections etc

This commit is contained in:
lingdocs 2022-06-01 19:04:09 -05:00
parent bb0e2daf0d
commit 4e5075ca19
22 changed files with 452 additions and 292 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@lingdocs/pashto-inflector", "name": "@lingdocs/pashto-inflector",
"version": "2.7.3", "version": "2.8.0",
"author": "lingdocs.com", "author": "lingdocs.com",
"description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations", "description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations",
"homepage": "https://verbs.lingdocs.com", "homepage": "https://verbs.lingdocs.com",

View File

@ -16,11 +16,11 @@ function APPicker(props: {
onRemove: () => void, onRemove: () => void,
}) { }) {
const [type, setType] = useState<APType | undefined>(props.AP const [type, setType] = useState<APType | undefined>(props.AP
? props.AP.type ? props.AP.selection.type
: undefined); : undefined);
useEffect(() => { useEffect(() => {
setType(props.AP setType(props.AP
? props.AP.type ? props.AP.selection.type
: undefined); : undefined);
}, [props.AP]); }, [props.AP]);
function handleClear() { function handleClear() {
@ -73,15 +73,15 @@ function APPicker(props: {
{type === "adverb" ? {type === "adverb" ?
<AdverbPicker <AdverbPicker
entryFeeder={props.entryFeeder.adverbs} entryFeeder={props.entryFeeder.adverbs}
adjective={props.AP?.type === "adverb" ? props.AP : undefined} adjective={props.AP?.selection.type === "adverb" ? props.AP.selection : undefined}
opts={props.opts} opts={props.opts}
onChange={props.onChange} onChange={(a) => props.onChange(a ? { type: "AP", selection: a } : undefined)}
/> />
: type === "sandwich" ? : type === "sandwich" ?
<SandwichPicker <SandwichPicker
onChange={props.onChange} onChange={(a) => props.onChange(a ? { type: "AP", selection: a } : undefined)}
opts={props.opts} opts={props.opts}
sandwich={props.AP?.type === "sandwich" ? props.AP : undefined} sandwich={props.AP?.selection.type === "sandwich" ? props.AP.selection : undefined}
entryFeeder={props.entryFeeder} entryFeeder={props.entryFeeder}
phraseIsComplete={props.phraseIsComplete} phraseIsComplete={props.phraseIsComplete}
onExit={handleSandwichExit} onExit={handleSandwichExit}

View File

@ -49,10 +49,10 @@ function EPDisplay({ eps, opts, setOmitSubject }: {
{result.e && <div className="text-muted mt-3"> {result.e && <div className="text-muted mt-3">
{result.e.map((e, i) => <div key={i}>{e}</div>)} {result.e.map((e, i) => <div key={i}>{e}</div>)}
</div>} </div>}
{EP.predicate.selection.type === "participle" && <div style={{ maxWidth: "6 00px", margin: "0 auto" }} className="alert alert-warning mt-3 pt-4"> {EP.predicate.selection.selection.type === "participle" && <div style={{ maxWidth: "6 00px", margin: "0 auto" }} className="alert alert-warning mt-3 pt-4">
<p> NOTE: This means that the subject {renderedSubject.e ? `(${renderedSubject.e})` : ""} is <strong>the action/idea</strong> of <p> NOTE: This means that the subject {renderedSubject.selection.e ? `(${renderedSubject.selection.e})` : ""} is <strong>the action/idea</strong> of
{` `} {` `}
"{rendered.predicate.e ? rendered.predicate.e : "the particple"}".</p> "{rendered.predicate.selection.e ? rendered.predicate.selection.e : "the particple"}".</p>
<p>It <strong>does not</strong> mean that the subject is doing the action, which is what the transaltion sounds like in English.</p> <p>It <strong>does not</strong> mean that the subject is doing the action, which is what the transaltion sounds like in English.</p>
</div>} </div>}
</div> </div>

View File

@ -38,7 +38,7 @@ function EPExplorer(props: {
entryFeeder: T.EntryFeeder, entryFeeder: T.EntryFeeder,
}) { }) {
const [mode, setMode] = useStickyState<"charts" | "phrases">("charts", "EPExplorerMode"); const [mode, setMode] = useStickyState<"charts" | "phrases">("charts", "EPExplorerMode");
const [eps, adjustEps] = useStickyReducer(epsReducer, blankEps, "EPState4", flashMessage); const [eps, adjustEps] = useStickyReducer(epsReducer, blankEps, "EPState5", flashMessage);
const [alert, setAlert] = useState<string | undefined>(undefined); const [alert, setAlert] = useState<string | undefined>(undefined);
const [showClipped, setShowClipped] = useState<string>(""); const [showClipped, setShowClipped] = useState<string>("");
const parent = useRef<HTMLDivElement>(null); const parent = useRef<HTMLDivElement>(null);
@ -57,7 +57,7 @@ function EPExplorer(props: {
// eslint-disable-next-line // eslint-disable-next-line
}, []); }, []);
const subject = getSubjectSelection(eps.blocks).selection; const subject = getSubjectSelection(eps.blocks).selection;
const king = subject?.type === "pronoun" const king = subject?.selection.type === "pronoun"
? "subject" ? "subject"
: eps.predicate.type === "Complement" : eps.predicate.type === "Complement"
? "subject" ? "subject"

View File

@ -65,15 +65,15 @@ export default function epsReducer(eps: T.EPSelectionState, action: EpsReducerAc
blocks: adjustSubjectSelection(eps.blocks, subject), blocks: adjustSubjectSelection(eps.blocks, subject),
}; };
} }
if (subject.type === "pronoun" && eps.predicate.type === "NP" && eps.predicate.NP?.type === "noun" && isUnisexNounEntry(eps.predicate.NP.entry)) { if (subject.selection.type === "pronoun" && eps.predicate.type === "NP" && eps.predicate.NP?.selection.type === "noun" && isUnisexNounEntry(eps.predicate.NP.selection.entry)) {
const predicate = eps.predicate.NP; const predicate = eps.predicate.NP.selection;
const adjusted = { const adjusted = {
...predicate, ...predicate,
...predicate.numberCanChange ? { ...predicate.numberCanChange ? {
number: personNumber(subject.person), number: personNumber(subject.selection.person),
} : {}, } : {},
...predicate.genderCanChange ? { ...predicate.genderCanChange ? {
gender: personGender(subject.person), gender: personGender(subject.selection.person),
} : {}, } : {},
} }
return { return {
@ -81,7 +81,10 @@ export default function epsReducer(eps: T.EPSelectionState, action: EpsReducerAc
blocks: adjustSubjectSelection(eps.blocks, subject), blocks: adjustSubjectSelection(eps.blocks, subject),
predicate: { predicate: {
...eps.predicate, ...eps.predicate,
NP: adjusted, NP: {
type: "NP",
selection: adjusted,
},
}, },
}; };
} }
@ -103,15 +106,18 @@ export default function epsReducer(eps: T.EPSelectionState, action: EpsReducerAc
}; };
} }
const subject = getSubjectSelection(eps.blocks).selection; const subject = getSubjectSelection(eps.blocks).selection;
if (subject?.type === "pronoun" && selection.type === "noun" && isUnisexNounEntry(selection.entry)) { if (subject?.selection.type === "pronoun" && selection.selection.type === "noun" && isUnisexNounEntry(selection.selection.entry)) {
const { gender, number } = selection; const { gender, number } = selection.selection;
const pronoun = subject.person; const pronoun = subject.selection.person;
const newPronoun = movePersonNumber(movePersonGender(pronoun, gender), number); const newPronoun = movePersonNumber(movePersonGender(pronoun, gender), number);
return { return {
...eps, ...eps,
blocks: adjustSubjectSelection(eps.blocks, { blocks: adjustSubjectSelection(eps.blocks, {
...subject, type: "NP",
selection: {
...subject.selection,
person: newPronoun, person: newPronoun,
},
}), }),
predicate: { predicate: {
...eps.predicate, ...eps.predicate,

View File

@ -15,11 +15,11 @@ function EqCompPicker(props: {
entryFeeder: T.EntryFeeder, entryFeeder: T.EntryFeeder,
}) { }) {
const [compType, setCompType] = useState<T.EqCompType | undefined>(props.comp const [compType, setCompType] = useState<T.EqCompType | undefined>(props.comp
? props.comp.type ? props.comp.selection.type
: undefined); : undefined);
useEffect(() => { useEffect(() => {
setCompType(props.comp setCompType(props.comp
? props.comp.type ? props.comp.selection.type
: undefined); : undefined);
}, [props.comp]); }, [props.comp]);
function handleClear() { function handleClear() {
@ -68,23 +68,23 @@ function EqCompPicker(props: {
{compType === "adjective" ? {compType === "adjective" ?
<AdjectivePicker <AdjectivePicker
entryFeeder={props.entryFeeder} entryFeeder={props.entryFeeder}
adjective={props.comp?.type === "adjective" ? props.comp : undefined} adjective={props.comp?.selection.type === "adjective" ? props.comp.selection : undefined}
opts={props.opts} opts={props.opts}
onChange={props.onChange} onChange={(a) => props.onChange(a ? { type: "EQComp", selection: a } : undefined)}
phraseIsComplete={props.phraseIsComplete} phraseIsComplete={props.phraseIsComplete}
/> />
: compType === "loc. adv." : compType === "loc. adv."
? <LocativeAdverbPicker ? <LocativeAdverbPicker
entryFeeder={props.entryFeeder.locativeAdverbs} entryFeeder={props.entryFeeder.locativeAdverbs}
adjective={props.comp?.type === "loc. adv." ? props.comp : undefined} adjective={props.comp?.selection.type === "loc. adv." ? props.comp.selection : undefined}
opts={props.opts} opts={props.opts}
onChange={props.onChange} onChange={(a) => props.onChange(a ? { type: "EQComp", selection: a } : undefined)}
/> />
: compType === "sandwich" : compType === "sandwich"
? <SandwichPicker ? <SandwichPicker
onChange={props.onChange} onChange={(a) => props.onChange(a ? { type: "EQComp", selection: a } : undefined)}
opts={props.opts} opts={props.opts}
sandwich={props.comp?.type === "sandwich" ? props.comp : undefined} sandwich={props.comp?.selection.type === "sandwich" ? props.comp.selection : undefined}
entryFeeder={props.entryFeeder} entryFeeder={props.entryFeeder}
onExit={handleSandwichExit} onExit={handleSandwichExit}
// TODO: get phraseIsComplete working here // TODO: get phraseIsComplete working here

View File

@ -25,19 +25,19 @@ function NPPicker(props: {
phraseIsComplete: boolean, phraseIsComplete: boolean,
isShrunk?: boolean, isShrunk?: boolean,
}) { }) {
if (props.is2ndPersonPicker && ((props.np?.type !== "pronoun") || !isSecondPerson(props.np.person))) { if (props.is2ndPersonPicker && ((props.np?.selection.type !== "pronoun") || !isSecondPerson(props.np.selection.person))) {
throw new Error("can't use 2ndPerson NPPicker without a pronoun"); throw new Error("can't use 2ndPerson NPPicker without a pronoun");
} }
const [addingPoss, setAddingPoss] = useState<boolean>(false); const [addingPoss, setAddingPoss] = useState<boolean>(false);
const [npType, setNpType] = useState<T.NPType | undefined>(props.np ? props.np.type : undefined); const [npType, setNpType] = useState<T.NPType | undefined>(props.np ? props.np.selection.type : undefined);
const onChange = (np: T.NPSelection | undefined) => { const onChange = (np: T.NPSelection | undefined) => {
props.onChange(ensureSingleShrink(props.np, np)) props.onChange(ensureSingleShrink(props.np, np))
} }
useEffect(() => { useEffect(() => {
setNpType(props.np ? props.np.type : undefined); setNpType(props.np ? props.np.selection.type : undefined);
}, [props.np]); }, [props.np]);
function handleClear() { function handleClear() {
if (props.np && props.np.type === "noun" && props.np.dynamicComplement) return; if (props.np && props.np.selection.type === "noun" && props.np.selection.dynamicComplement) return;
setNpType(undefined); setNpType(undefined);
onChange(undefined); onChange(undefined);
} }
@ -50,7 +50,7 @@ function NPPicker(props: {
distance: "far", distance: "far",
}; };
setNpType(ntp); setNpType(ntp);
onChange(pronoun); onChange({ type: "NP", selection: pronoun });
} else { } else {
if (props.np) { if (props.np) {
onChange(undefined); onChange(undefined);
@ -60,39 +60,48 @@ function NPPicker(props: {
} }
// TODO: REMOVE // TODO: REMOVE
function handlePossesiveChange(p: T.NPSelection | undefined) { function handlePossesiveChange(p: T.NPSelection | undefined) {
if (!props.np || props.np.type === "pronoun") return; if (!props.np || props.np.selection.type === "pronoun") return;
if (!p) { if (!p) {
onChange({ onChange({
...props.np, type: "NP",
selection: {
...props.np.selection,
possesor: undefined, possesor: undefined,
},
}); });
return; return;
} }
const isNewPosesser = checkForNewPossesor(p, props.np.possesor); const isNewPosesser = checkForNewPossesor(p, props.np.selection.possesor);
const possesor: T.PossesorSelection = { const possesor: T.PossesorSelection = {
np: p, np: p,
shrunken: (!isNewPosesser && props.np.possesor) ? props.np.possesor.shrunken : false, shrunken: (!isNewPosesser && props.np.selection.possesor) ? props.np.selection.possesor.shrunken : false,
}; };
onChange({ onChange({
...props.np, type: "NP",
selection: {
...props.np.selection,
possesor, possesor,
});
}
function handleToggleShrunken() {
if (!props.np || props.np.type === "pronoun" || !props.np.possesor || !props.phraseIsComplete) return;
onChange({
...props.np,
possesor: {
...props.np.possesor,
shrunken: !props.np.possesor.shrunken,
}, },
}); });
} }
const isDynamicComplement = props.np && props.np.type === "noun" && props.np.dynamicComplement; function handleToggleShrunken() {
if (!props.np || props.np.selection.type === "pronoun" || !props.np.selection.possesor || !props.phraseIsComplete) return;
onChange({
type: "NP",
selection: {
...props.np.selection,
possesor: {
...props.np.selection.possesor,
shrunken: !props.np.selection.possesor.shrunken,
},
},
});
}
const isDynamicComplement = props.np && props.np.selection.type === "noun" && props.np.selection.dynamicComplement;
const clearButton = (!props.cantClear && !props.is2ndPersonPicker && !isDynamicComplement) const clearButton = (!props.cantClear && !props.is2ndPersonPicker && !isDynamicComplement)
? <button className="btn btn-sm btn-light mb-2" onClick={handleClear}>X</button> ? <button className="btn btn-sm btn-light mb-2" onClick={handleClear}>X</button>
: <div></div>; : <div></div>;
const possesiveLabel = props.np?.type === "participle" ? "Subj/Obj" : "Possesor"; const possesiveLabel = props.np?.selection.type === "participle" ? "Subj/Obj" : "Possesor";
return <> return <>
<div className="d-flex flex-row justify-content-between"> <div className="d-flex flex-row justify-content-between">
<div></div> <div></div>
@ -121,15 +130,15 @@ function NPPicker(props: {
</button> </button>
</div>)} </div>)}
</div>} </div>}
{(props.np && props.np.type !== "pronoun" && (props.np.possesor || addingPoss)) && <div className="mb-3" style={{ {(props.np && props.np.selection.type !== "pronoun" && (props.np.selection.possesor || addingPoss)) && <div className="mb-3" style={{
paddingLeft: "0.65rem", paddingLeft: "0.65rem",
borderLeft: "2px solid grey", borderLeft: "2px solid grey",
background: (props.np.possesor?.shrunken && !props.isShrunk) ? shrunkenBackground : "inherit", background: (props.np.selection.possesor?.shrunken && !props.isShrunk) ? shrunkenBackground : "inherit",
}}> }}>
<div className="d-flex flex-row text-muted mb-2"> <div className="d-flex flex-row text-muted mb-2">
<div>{possesiveLabel}:</div> <div>{possesiveLabel}:</div>
{(props.np.possesor && !props.isShrunk && props.phraseIsComplete) && <div className="clickable ml-3 mr-2" onClick={handleToggleShrunken}> {(props.np.selection.possesor && !props.isShrunk && props.phraseIsComplete) && <div className="clickable ml-3 mr-2" onClick={handleToggleShrunken}>
{!props.np.possesor.shrunken ? "🪄" : "👶"} {!props.np.selection.possesor.shrunken ? "🪄" : "👶"}
</div>} </div>}
<div className="clickable ml-2" onClick={() => { <div className="clickable ml-2" onClick={() => {
setAddingPoss(false); setAddingPoss(false);
@ -143,7 +152,7 @@ function NPPicker(props: {
onChange={handlePossesiveChange} onChange={handlePossesiveChange}
counterPart={undefined} counterPart={undefined}
cantClear cantClear
np={props.np.possesor ? props.np.possesor.np : undefined} np={props.np.selection.possesor ? props.np.selection.possesor.np : undefined}
role="possesor" role="possesor"
opts={props.opts} opts={props.opts}
entryFeeder={props.entryFeeder} entryFeeder={props.entryFeeder}
@ -152,11 +161,11 @@ function NPPicker(props: {
{(npType === "noun" || npType === "participle") && props.np && !addingPoss && <div> {(npType === "noun" || npType === "participle") && props.np && !addingPoss && <div>
<span className="clickable text-muted" onClick={() => setAddingPoss(true)}>+ {possesiveLabel}</span> <span className="clickable text-muted" onClick={() => setAddingPoss(true)}>+ {possesiveLabel}</span>
</div>} </div>}
{(npType === "pronoun" && props.np?.type === "pronoun") {(npType === "pronoun" && props.np?.selection.type === "pronoun")
? <PronounPicker ? <PronounPicker
role={props.role} role={props.role}
pronoun={props.np} pronoun={props.np.selection}
onChange={onChange} onChange={(p) => onChange({ type: "NP", selection: p })}
is2ndPersonPicker={props.is2ndPersonPicker} is2ndPersonPicker={props.is2ndPersonPicker}
opts={props.opts} opts={props.opts}
/> />
@ -164,15 +173,15 @@ function NPPicker(props: {
? <NounPicker ? <NounPicker
phraseIsComplete={props.phraseIsComplete} phraseIsComplete={props.phraseIsComplete}
entryFeeder={props.entryFeeder} entryFeeder={props.entryFeeder}
noun={(props.np && props.np.type === "noun") ? props.np : undefined} noun={(props.np && props.np.selection.type === "noun") ? props.np.selection : undefined}
onChange={onChange} onChange={(s) => onChange(s ? { type: "NP", selection: s } : undefined)}
opts={props.opts} opts={props.opts}
/> />
: npType === "participle" : npType === "participle"
? <ParticiplePicker ? <ParticiplePicker
entryFeeder={props.entryFeeder.verbs} entryFeeder={props.entryFeeder.verbs}
participle={(props.np && props.np.type === "participle") ? props.np : undefined} participle={(props.np && props.np.selection.type === "participle") ? props.np.selection : undefined}
onChange={onChange} onChange={(s) => onChange(s ? { type: "NP", selection: s } : undefined)}
opts={props.opts} opts={props.opts}
/> />
: null : null
@ -184,44 +193,53 @@ function NPPicker(props: {
function ensureSingleShrink(old: T.NPSelection | undefined, s: T.NPSelection | undefined): T.NPSelection | undefined { function ensureSingleShrink(old: T.NPSelection | undefined, s: T.NPSelection | undefined): T.NPSelection | undefined {
if (!s) return s; if (!s) return s;
function countShrinks(np: T.NPSelection): number { function countShrinks(np: T.NPSelection): number {
if (np.type === "pronoun") return 0; if (np.selection.type === "pronoun") return 0;
if (!np.possesor) return 0; if (!np.selection.possesor) return 0;
return (np.possesor.shrunken ? 1 : 0) + countShrinks(np.possesor.np); return (np.selection.possesor.shrunken ? 1 : 0) + countShrinks(np.selection.possesor.np);
} }
function keepNewShrink(old: T.NPSelection, n: T.NPSelection): T.NPSelection { function keepNewShrink(old: T.NPSelection, n: T.NPSelection): T.NPSelection {
if (n.type === "pronoun") return n; if (n.selection.type === "pronoun") return n;
if (old.type === "pronoun" || !n.possesor || !old.possesor) return n; if (old.selection.type === "pronoun" || !n.selection.possesor || !old.selection.possesor) return n;
if (n.possesor.shrunken && !old.possesor.shrunken) { if (n.selection.possesor.shrunken && !old.selection.possesor.shrunken) {
return { return {
...n, type: "NP",
selection: {
...n.selection,
possesor: { possesor: {
...n.possesor, ...n.selection.possesor,
np: removeShrinks(n.possesor.np), np: removeShrinks(n.selection.possesor.np),
},
}, },
}; };
} }
return { return {
...n, type: "NP",
selection:{
...n.selection,
possesor: { possesor: {
shrunken: false, shrunken: false,
np: keepNewShrink(old.possesor.np, n.possesor.np), np: keepNewShrink(old.selection.possesor.np, n.selection.possesor.np),
}, },
} },
};
} }
function removeShrinks(n: T.NPSelection): T.NPSelection { function removeShrinks(n: T.NPSelection): T.NPSelection {
if (n.type === "pronoun") return n; if (n.selection.type === "pronoun") return n;
if (!n.possesor) return n; if (!n.selection.possesor) return n;
return { return {
...n, type: "NP",
selection: {
...n.selection,
possesor: { possesor: {
shrunken: false, shrunken: false,
np: removeShrinks(n.possesor.np), np: removeShrinks(n.selection.possesor.np),
},
}, },
}; };
} }
if (!old) return s; if (!old) return s;
if (s.type === "pronoun") return s; if (s.selection.type === "pronoun") return s;
if (!s.possesor) return s; if (!s.selection.possesor) return s;
const numOfShrinks = countShrinks(s); const numOfShrinks = countShrinks(s);
if (numOfShrinks < 2) return s; if (numOfShrinks < 2) return s;
return keepNewShrink(old, s); return keepNewShrink(old, s);
@ -234,12 +252,12 @@ function checkForNewPossesor(n: T.NPSelection | undefined, old: T.PossesorSelect
if (n.type !== old.np.type) { if (n.type !== old.np.type) {
return true; return true;
} }
if (n.type === "pronoun") return false; if (n.selection.type === "pronoun") return false;
if (n.type === "noun" && old.np.type === "noun") { if (n.selection.type === "noun" && old.np.selection.type === "noun") {
return n.entry.ts !== old.np.entry.ts; return n.selection.entry.ts !== old.np.selection.entry.ts;
} }
if (n.type === "participle" && old.np.type === "participle") { if (n.selection.type === "participle" && old.np.selection.type === "participle") {
return n.verb.entry.ts !== old.np.verb.entry.ts; return n.selection.verb.entry.ts !== old.np.selection.verb.entry.ts;
} }
return false; return false;
} }

View File

@ -44,7 +44,7 @@ function VPExplorer(props: {
props.loaded props.loaded
? props.loaded ? props.loaded
: savedVps => makeVPSelectionState(props.verb, savedVps), : savedVps => makeVPSelectionState(props.verb, savedVps),
"vpsState12", "vpsState14",
flashMessage, flashMessage,
); );
const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">( const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">(
@ -217,7 +217,7 @@ function VPExplorer(props: {
<i className="fas fa-chevron-right" /> <i className="fas fa-chevron-right" />
</div> : <div/>} </div> : <div/>}
</div> </div>
{(!block || block.type === "adverb" || block.type === "sandwich") {(!block || block.type === "AP")
? <APPicker ? <APPicker
phraseIsComplete={phraseIsComplete} phraseIsComplete={phraseIsComplete}
heading="AP" heading="AP"

View File

@ -269,9 +269,9 @@ function QuizNPDisplay({ children, stage, opts }: {
? <div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div> ? <div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div>
: <div className="text-centered" style={{ fontSize: "large" }}> : <div className="text-centered" style={{ fontSize: "large" }}>
{stage === "blanks" && <div> {stage === "blanks" && <div>
<InlinePs opts={opts}>{children.ps[0]}</InlinePs> <InlinePs opts={opts}>{children.selection.ps[0]}</InlinePs>
</div>} </div>}
<div>{children.e}</div> <div>{children.selection.e}</div>
</div>} </div>}
</div>; </div>;
} }
@ -377,11 +377,11 @@ function getOptionFromResult(r: {
function completeVPs(vps: T.VPSelectionState): T.VPSelectionComplete { function completeVPs(vps: T.VPSelectionState): T.VPSelectionComplete {
const vpsSubj = getSubjectSelection(vps.blocks).selection; const vpsSubj = getSubjectSelection(vps.blocks).selection;
const vpsObj = getObjectSelection(vps.blocks).selection; const vpsObj = getObjectSelection(vps.blocks).selection;
const oldSubj = vpsSubj?.type === "pronoun" const oldSubj = vpsSubj?.selection.type === "pronoun"
? vpsSubj.person ? vpsSubj.selection.person
: undefined; : undefined;
const oldObj = (typeof vpsObj === "object" && vpsObj.type === "pronoun") const oldObj = (typeof vpsObj === "object" && vpsObj.selection.type === "pronoun")
? vpsObj.person ? vpsObj.selection.person
: undefined; : undefined;
const { subj, obj } = randomSubjObj( const { subj, obj } = randomSubjObj(
oldSubj === undefined oldSubj === undefined
@ -400,20 +400,26 @@ function completeVPs(vps: T.VPSelectionState): T.VPSelectionComplete {
...vps, ...vps,
blocks: adjustObjectSelection( blocks: adjustObjectSelection(
adjustSubjectSelection(vps.blocks, { adjustSubjectSelection(vps.blocks, {
type: "NP",
selection: {
type: "pronoun", type: "pronoun",
distance: "far", distance: "far",
person: subj, person: subj,
},
}), }),
( (
(typeof vpsObj === "object" && !(vpsObj.type === "noun" && vpsObj.dynamicComplement)) (typeof vpsObj === "object" && !(vpsObj.selection.type === "noun" && vpsObj.selection.dynamicComplement))
|| ||
vpsObj === undefined vpsObj === undefined
) )
? { ? {
type: "NP",
selection: {
type: "pronoun", type: "pronoun",
distance: "far", distance: "far",
person: obj, person: obj,
} }
}
: vpsObj, : vpsObj,
), ),
verb, verb,
@ -426,30 +432,40 @@ function getRandomVPSelection(mix: MixType = "both") {
const subject = getSubjectSelection(VPS.blocks).selection; const subject = getSubjectSelection(VPS.blocks).selection;
const object = getObjectSelection(VPS.blocks).selection; const object = getObjectSelection(VPS.blocks).selection;
const verb = VPS.verb; const verb = VPS.verb;
const oldSubj = (subject.type === "pronoun") const oldSubj = (subject.selection.type === "pronoun")
? subject.person ? subject.selection.person
: undefined; : undefined;
const oldObj = (typeof object === "object" && object.type === "pronoun") const oldObj = (typeof object === "object" && object.selection.type === "pronoun")
? object.person ? object.selection.person
: undefined; : undefined;
const { subj, obj } = randomSubjObj( const { subj, obj } = randomSubjObj(
oldSubj !== undefined ? { subj: oldSubj, obj: oldObj } : undefined oldSubj !== undefined ? { subj: oldSubj, obj: oldObj } : undefined
); );
const randSubj: T.PronounSelection = subject?.type === "pronoun" ? { const randSubj: T.NPSelection = {
...subject, type: "NP",
selection: (
subject?.selection.type === "pronoun" ? {
...subject.selection,
person: subj, person: subj,
} : { } : {
type: "pronoun", type: "pronoun",
distance: "far", distance: "far",
person: subj, person: subj,
}
),
}; };
const randObj: T.PronounSelection = typeof object === "object" && object.type === "pronoun" ? { const randObj: T.NPSelection = {
...object, type: "NP",
selection: (
typeof object === "object" && object.selection.type === "pronoun" ? {
...object.selection,
person: obj, person: obj,
} : { } : {
type: "pronoun", type: "pronoun",
distance: "far", distance: "far",
person: obj, person: obj,
}
),
}; };
// ensure that the verb selection is complete // ensure that the verb selection is complete
if (mix === "tenses") { if (mix === "tenses") {
@ -466,7 +482,7 @@ function getRandomVPSelection(mix: MixType = "both") {
blocks: possibleShuffleArray(adjustObjectSelection( blocks: possibleShuffleArray(adjustObjectSelection(
adjustSubjectSelection(VPS.blocks, randSubj), adjustSubjectSelection(VPS.blocks, randSubj),
( (
(typeof object === "object" && !(object.type === "noun" && object.dynamicComplement)) (typeof object === "object" && !(object.selection.type === "noun" && object.selection.dynamicComplement))
|| ||
object === undefined object === undefined
) )

View File

@ -12,7 +12,7 @@ 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?.blocks ? getSubjectSelection(os.blocks) : undefined); : (os?.blocks ? getSubjectSelection(os.blocks).selection : undefined);
function getTransObjFromos() { function getTransObjFromos() {
const osObj = os ? getObjectSelection(os.blocks).selection : undefined; const osObj = os ? getObjectSelection(os.blocks).selection : undefined;
if ( if (
@ -20,7 +20,7 @@ export function makeVPSelectionState(
osObj === "none" || osObj === "none" ||
typeof osObj === "number" || typeof osObj === "number" ||
os.verb.isCompound === "dynamic" || os.verb.isCompound === "dynamic" ||
(osObj?.type === "noun" && osObj.dynamicComplement) (osObj?.selection.type === "noun" && osObj.selection.dynamicComplement)
) return undefined; ) return undefined;
return osObj; return osObj;
} }
@ -85,7 +85,7 @@ export function changeStatDyn(v: T.VPSelectionState, s: "dynamic" | "stative"):
blocks: adjustObjectSelection( blocks: adjustObjectSelection(
v.blocks, v.blocks,
s === "dynamic" s === "dynamic"
? makeNounSelection(info.dynamic.objComplement.entry as T.NounEntry, undefined, true) ? { type: "NP", selection: makeNounSelection(info.dynamic.objComplement.entry as T.NounEntry, undefined, true) }
: undefined, : undefined,
), ),
verb: { verb: {

View File

@ -285,8 +285,8 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
} }
function hasPronounConflict(subject: T.NPSelection | undefined, object: undefined | T.VerbObject): boolean { function hasPronounConflict(subject: T.NPSelection | undefined, object: undefined | T.VerbObject): boolean {
const subjPronoun = (subject && subject.type === "pronoun") ? subject : undefined; const subjPronoun = (subject && subject.selection.type === "pronoun") ? subject.selection : undefined;
const objPronoun = (object && typeof object === "object" && object.type === "pronoun") ? object : undefined; const objPronoun = (object && typeof object === "object" && object.selection.type === "pronoun") ? object.selection : undefined;
if (!subjPronoun || !objPronoun) return false; if (!subjPronoun || !objPronoun) return false;
return isInvalidSubjObjCombo(subjPronoun.person, objPronoun.person); return isInvalidSubjObjCombo(subjPronoun.person, objPronoun.person);
} }

View File

@ -11,13 +11,13 @@ export function randomPerson(a?: { prev?: T.Person, counterPart?: T.VerbObject |
if (!a) { if (!a) {
return getRandPers(); return getRandPers();
} }
if (a.counterPart !== undefined && typeof a.counterPart === "object" && a.counterPart.type === "pronoun") { if (a.counterPart !== undefined && typeof a.counterPart === "object" && a.counterPart.selection.type === "pronoun") {
// with counterpart pronoun // with counterpart pronoun
let newP = 0; let newP = 0;
do { do {
newP = getRandPers(); newP = getRandPers();
} while ( } while (
isInvalidSubjObjCombo(a.counterPart.person, newP) isInvalidSubjObjCombo(a.counterPart.selection.person, newP)
|| ||
(newP === a.prev) (newP === a.prev)
); );

View File

@ -56,23 +56,59 @@ export function makeAPBlock(): { key: number, block: undefined } {
}; };
} }
export function makeSubjectSelection(selection: T.SubjectSelection | T.NPSelection | undefined): T.SubjectSelection { export function makeSubjectSelection(selection: T.SubjectSelection | T.NPSelection | T.NPSelection["selection"] | undefined): T.SubjectSelection {
if (selection?.type === "subjectSelection") { if (!selection) {
return {
type: "subjectSelection",
selection: undefined,
};
}
if (selection.type === "subjectSelection") {
return selection; return selection;
} }
if (selection.type === "NP") {
return { return {
type: "subjectSelection", type: "subjectSelection",
selection, selection,
}; };
}
return {
type: "subjectSelection",
selection: {
type: "NP",
selection,
}
};
} }
export function makeObjectSelection(selection: T.ObjectSelection | T.ObjectNP | T.NPSelection | undefined): T.ObjectSelection { export function makeObjectSelection(selection: T.ObjectSelection | T.ObjectNP | T.NPSelection | T.NPSelection["selection"] | undefined): T.ObjectSelection {
if (selection && typeof selection === "object" && selection.type === "objectSelection") { if (!selection) {
return {
type: "objectSelection",
selection: undefined,
}
}
if (typeof selection !== "object") {
return {
type: "objectSelection",
selection,
};
}
if (selection.type === "objectSelection") {
return selection; return selection;
} }
if (selection.type === "NP") {
return {
type: "objectSelection",
selection,
};
}
return { return {
type: "objectSelection", type: "objectSelection",
selection: selection, selection: {
type: "NP",
selection,
},
}; };
} }

View File

@ -125,7 +125,7 @@ export function getVPSegmentsAndKids(VP: T.VPRendered, form?: T.FormVersion): {
if (typeof block.selection !== "object") return accum; if (typeof block.selection !== "object") return accum;
return [ return [
...accum, ...accum,
makeSegment(getPashtoFromRendered(block.selection, subject.person)), makeSegment(getPashtoFromRendered(block.selection, subject.selection.person)),
] ]
} }
return [ return [
@ -478,8 +478,8 @@ export function findPossesivesToShrinkInVP(VP: T.VPRendered, f: {
}): T.Rendered<T.NPSelection>[] { }): T.Rendered<T.NPSelection>[] {
return VP.blocks.reduce((found, block) => { return VP.blocks.reduce((found, block) => {
if (block.type === "subjectSelection") { if (block.type === "subjectSelection") {
if (block.selection.role === "king" && f.removedKing) return found; if (block.selection.selection.role === "king" && f.removedKing) return found;
if (block.selection.role === "servant" && f.shrunkServant) return found; if (block.selection.selection.role === "servant" && f.shrunkServant) return found;
return [ return [
...findPossesivesInNP(block.selection), ...findPossesivesInNP(block.selection),
...found, ...found,
@ -487,19 +487,19 @@ export function findPossesivesToShrinkInVP(VP: T.VPRendered, f: {
} }
if (block.type === "objectSelection") { if (block.type === "objectSelection") {
if (typeof block.selection !== "object") return found; if (typeof block.selection !== "object") return found;
if (block.selection.role === "king" && f.removedKing) return found; if (block.selection.selection.role === "king" && f.removedKing) return found;
if (block.selection.role === "servant" && f.shrunkServant) return found; if (block.selection.selection.role === "servant" && f.shrunkServant) return found;
return [ return [
...findPossesivesInNP(block.selection), ...findPossesivesInNP(block.selection),
...found, ...found,
]; ];
} }
if (block.type === "sandwich") { if (block.selection.type === "sandwich") {
if (block.inside.type === "pronoun") { if (block.selection.inside.selection.type === "pronoun") {
return found; return found;
} }
return [ return [
...findPossesivesInNP(block.inside), ...findPossesivesInNP(block.selection.inside),
...found, ...found,
]; ];
} }
@ -510,18 +510,18 @@ export function findPossesivesToShrinkInVP(VP: T.VPRendered, f: {
function findPossesivesInNP(NP: T.Rendered<T.NPSelection> | T.ObjectNP | undefined): T.Rendered<T.NPSelection>[] { function findPossesivesInNP(NP: T.Rendered<T.NPSelection> | T.ObjectNP | undefined): T.Rendered<T.NPSelection>[] {
if (NP === undefined) return []; if (NP === undefined) return [];
if (typeof NP !== "object") return []; if (typeof NP !== "object") return [];
if (!NP.possesor) return []; if (!NP.selection.possesor) return [];
if (NP.adjectives) { if (NP.selection.adjectives) {
const { adjectives, ...rest } = NP; const { adjectives, ...rest } = NP.selection;
return [ return [
...findPossesivesInAdjectives(adjectives), ...findPossesivesInAdjectives(adjectives),
...findPossesivesInNP(rest), ...findPossesivesInNP({ type: "NP", selection: rest }),
]; ];
} }
if (NP.possesor.shrunken) { if (NP.selection.possesor.shrunken) {
return [NP.possesor.np]; return [NP.selection.possesor.np];
} }
return findPossesivesInNP(NP.possesor.np); return findPossesivesInNP(NP.selection.possesor.np);
} }
function findPossesivesInAdjectives(a: T.Rendered<T.AdjectiveSelection>[]): T.Rendered<T.NPSelection>[] { function findPossesivesInAdjectives(a: T.Rendered<T.AdjectiveSelection>[]): T.Rendered<T.NPSelection>[] {
@ -551,8 +551,8 @@ export function findPossesivesToShrinkInEP(EP: T.EPRendered): FoundNP[] {
...found, ...found,
]; ];
} }
if (block.type === "sandwich") { if (block.selection.type === "sandwich") {
const found: FoundNP[] = findPossesivesInNP(block.inside).map(np => ({ np, from: "AP" })); const found: FoundNP[] = findPossesivesInNP(block.selection.inside).map(np => ({ np, from: "AP" }));
return [ return [
...accum, ...accum,
...found, ...found,
@ -560,15 +560,15 @@ export function findPossesivesToShrinkInEP(EP: T.EPRendered): FoundNP[] {
} }
return accum; return accum;
}, [] as FoundNP[]); }, [] as FoundNP[]);
const inPredicate: FoundNP[] = ((EP.predicate.type === "loc. adv.") const inPredicate: FoundNP[] = ((EP.predicate.selection.type === "loc. adv.")
? [] ? []
: (EP.predicate.type === "adjective") : (EP.predicate.selection.type === "adjective")
? findPossesivesInAdjective(EP.predicate) ? findPossesivesInAdjective(EP.predicate.selection)
: EP.predicate.type === "sandwich" : EP.predicate.selection.type === "sandwich"
? findPossesivesInNP(EP.predicate.inside) ? findPossesivesInNP(EP.predicate.selection.inside)
: EP.predicate.type === "pronoun" : EP.predicate.selection.type === "pronoun"
? [] ? []
: findPossesivesInNP(EP.predicate)).map(np => ({ np, from: "predicate" })); : findPossesivesInNP({ type: "NP", selection: EP.predicate.selection })).map(np => ({ np, from: "predicate" }));
return [ return [
...inBlocks, ...inBlocks,
...inPredicate, ...inPredicate,
@ -587,11 +587,11 @@ export function findPossesivesToShrinkInEP(EP: T.EPRendered): FoundNP[] {
export function shrinkNP(np: T.Rendered<T.NPSelection>): Segment { export function shrinkNP(np: T.Rendered<T.NPSelection>): Segment {
function getFirstSecThird(): 1 | 2 | 3 { function getFirstSecThird(): 1 | 2 | 3 {
if ([0, 1, 6, 7].includes(np.person)) return 1; if ([0, 1, 6, 7].includes(np.selection.person)) return 1;
if ([2, 3, 8, 9].includes(np.person)) return 2; if ([2, 3, 8, 9].includes(np.selection.person)) return 2;
return 3; return 3;
} }
const [row, col] = getVerbBlockPosFromPerson(np.person); const [row, col] = getVerbBlockPosFromPerson(np.selection.person);
return makeSegment(pronouns.mini[row][col], ["isKid", getFirstSecThird()]); return makeSegment(pronouns.mini[row][col], ["isKid", getFirstSecThird()]);
} }

View File

@ -5,15 +5,15 @@ import {
import * as T from "../../types"; import * as T from "../../types";
import { concatPsString } from "../p-text-helpers"; import { concatPsString } from "../p-text-helpers";
function getBaseAndAdjectives(np: T.Rendered<T.NPSelection | T.EqCompSelection | T.APSelection>): T.PsString[] { function getBaseAndAdjectives({ selection }: T.Rendered<T.NPSelection | T.EqCompSelection | T.APSelection>): T.PsString[] {
if (np.type === "sandwich") { if (selection.type === "sandwich") {
return getSandwichPsBaseAndAdjectives(np); return getSandwichPsBaseAndAdjectives(selection);
} }
const adjs = "adjectives" in np && np.adjectives; const adjs = "adjectives" in selection && selection.adjectives;
if (!adjs) { if (!adjs) {
return np.ps; return selection.ps;
} }
return np.ps.map(p => ( return selection.ps.map(p => (
concatPsString( concatPsString(
adjs.reduce((accum, curr) => ( adjs.reduce((accum, curr) => (
// TODO: with variations of adjs? // TODO: with variations of adjs?
@ -27,10 +27,10 @@ function getBaseAndAdjectives(np: T.Rendered<T.NPSelection | T.EqCompSelection |
function getSandwichPsBaseAndAdjectives(s: T.Rendered<T.SandwichSelection<T.Sandwich>>): T.PsString[] { function getSandwichPsBaseAndAdjectives(s: T.Rendered<T.SandwichSelection<T.Sandwich>>): T.PsString[] {
const insideBase = getBaseAndAdjectives(s.inside); const insideBase = getBaseAndAdjectives(s.inside);
const willContractWithPronoun = s.before && s.before.p === "د" && s.inside.type === "pronoun" const willContractWithPronoun = s.before && s.before.p === "د" && s.inside.selection.type === "pronoun"
&& (isFirstPerson(s.inside.person) || isSecondPerson(s.inside.person)) && (isFirstPerson(s.inside.selection.person) || isSecondPerson(s.inside.selection.person))
const contracted = (willContractWithPronoun && s.inside.type === "pronoun") const contracted = (willContractWithPronoun && s.inside.selection.type === "pronoun")
? contractPronoun(s.inside) ? contractPronoun(s.inside.selection)
: undefined : undefined
return insideBase.map((inside) => ( return insideBase.map((inside) => (
concatPsString( concatPsString(
@ -52,54 +52,63 @@ function contractPronoun(n: T.Rendered<T.PronounSelection>): T.PsString | undefi
} }
function trimOffShrunkenPossesive(p: T.Rendered<T.NPSelection>): T.Rendered<T.NPSelection> { function trimOffShrunkenPossesive(p: T.Rendered<T.NPSelection>): T.Rendered<T.NPSelection> {
if (!("possesor" in p)) { if (!("possesor" in p.selection)) {
return p; return p;
} }
if (!p.possesor) { if (!p.selection.possesor) {
return p; return p;
} }
if (p.possesor.shrunken) { if (p.selection.possesor.shrunken) {
return { return {
...p, type: "NP",
selection: {
...p.selection,
possesor: undefined, possesor: undefined,
},
}; };
} }
return { return {
...p, type: "NP",
selection: {
...p.selection,
possesor: { possesor: {
...p.possesor, ...p.selection.possesor,
np: trimOffShrunkenPossesive(p.possesor.np), np: trimOffShrunkenPossesive(p.selection.possesor.np),
} },
} },
};
} }
export function getPashtoFromRendered(b: T.Rendered<T.NPSelection> | T.Rendered<T.EqCompSelection> | T.Rendered<T.APSelection>, subjectsPerson: false | T.Person): T.PsString[] { export function getPashtoFromRendered(b: T.Rendered<T.NPSelection> | T.Rendered<T.EqCompSelection> | T.Rendered<T.APSelection>, subjectsPerson: false | T.Person): T.PsString[] {
const base = getBaseAndAdjectives(b); const base = getBaseAndAdjectives(b);
if (b.type === "loc. adv." || b.type === "adverb") { if (b.selection.type === "loc. adv." || b.selection.type === "adverb") {
return base; return base;
} }
if (b.type === "adjective") { if (b.selection.type === "adjective") {
if (!b.sandwich) { if (!b.selection.sandwich) {
return base return base
} }
const sandwichPs = getPashtoFromRendered(b.sandwich, false); const sandwichPs = getPashtoFromRendered({ type: "AP", selection: b.selection.sandwich }, false);
return base.flatMap(p => ( return base.flatMap(p => (
sandwichPs.flatMap(s => ( sandwichPs.flatMap(s => (
concatPsString(s, " ", p) concatPsString(s, " ", p)
)) ))
)); ));
} }
const trimmed = b.type === "sandwich" ? { const trimmed = b.selection.type === "sandwich" ? {
...b, type: b.type,
inside: trimOffShrunkenPossesive(b.inside), selection: {
} : trimOffShrunkenPossesive(b); ...b.selection,
if (trimmed.type === "sandwich") { inside: trimOffShrunkenPossesive(b.selection.inside),
return trimmed.inside.possesor },
? addPossesor(trimmed.inside.possesor.np, base, subjectsPerson) } : trimOffShrunkenPossesive({ type: "NP", selection: b.selection });
if (trimmed.selection.type === "sandwich") {
return trimmed.selection.inside.selection.possesor
? addPossesor(trimmed.selection.inside.selection.possesor.np, base, subjectsPerson)
: base; : base;
} }
if (trimmed.possesor) { if (trimmed.selection.possesor) {
return addPossesor(trimmed.possesor.np, base, subjectsPerson); return addPossesor(trimmed.selection.possesor.np, base, subjectsPerson);
} }
return base; return base;
} }
@ -114,19 +123,19 @@ function addPossesor(owner: T.Rendered<T.NPSelection>, existing: T.PsString[], s
} }
const wPossesor = existing.flatMap(ps => ( const wPossesor = existing.flatMap(ps => (
getBaseAndAdjectives(owner).map(v => ( getBaseAndAdjectives(owner).map(v => (
(owner.type === "pronoun" && subjectsPerson !== false && willBeReflexive(subjectsPerson, owner.person)) (owner.selection.type === "pronoun" && subjectsPerson !== false && willBeReflexive(subjectsPerson, owner.selection.person))
? concatPsString({ p: "خپل", f: "khpul" }, " ", ps) ? concatPsString({ p: "خپل", f: "khpul" }, " ", ps)
: (owner.type === "pronoun" && isFirstPerson(owner.person)) : (owner.selection.type === "pronoun" && isFirstPerson(owner.selection.person))
? concatPsString({ p: "ز", f: "z" }, v, " ", ps) ? concatPsString({ p: "ز", f: "z" }, v, " ", ps)
: (owner.type === "pronoun" && isSecondPerson(owner.person)) : (owner.selection.type === "pronoun" && isSecondPerson(owner.selection.person))
? concatPsString({ p: "س", f: "s" }, v, " ", ps) ? concatPsString({ p: "س", f: "s" }, v, " ", ps)
: concatPsString({ p: "د", f: "du" }, " ", v, " ", ps) : concatPsString({ p: "د", f: "du" }, " ", v, " ", ps)
)) ))
)); ));
if (!owner.possesor) { if (!owner.selection.possesor) {
return wPossesor; return wPossesor;
} }
return addPossesor(owner.possesor.np, wPossesor, subjectsPerson); return addPossesor(owner.selection.possesor.np, wPossesor, subjectsPerson);
} }
function addArticlesAndAdjs(np: T.Rendered<T.NounSelection>): string | undefined { function addArticlesAndAdjs(np: T.Rendered<T.NounSelection>): string | undefined {
@ -156,10 +165,10 @@ function addPossesors(possesor: T.Rendered<T.NPSelection> | undefined, base: str
} }
if (!base) return undefined; if (!base) return undefined;
if (!possesor) return base; if (!possesor) return base;
if (possesor.type === "pronoun") { if (possesor.selection.type === "pronoun") {
return type === "noun" return type === "noun"
? `${pronounPossEng(possesor.person)} ${removeArticles(base)}` ? `${pronounPossEng(possesor.selection.person)} ${removeArticles(base)}`
: `(${pronounPossEng(possesor.person)}) ${removeArticles(base)} (${possesor.e})` : `(${pronounPossEng(possesor.selection.person)}) ${removeArticles(base)} (${possesor.selection.e})`
} }
const possesorE = getEnglishFromRendered(possesor); const possesorE = getEnglishFromRendered(possesor);
if (!possesorE) return undefined; if (!possesorE) return undefined;
@ -191,23 +200,23 @@ function pronounPossEng(p: T.Person): string {
} }
export function getEnglishFromRendered(r: T.Rendered<T.NPSelection | T.EqCompSelection | T.APSelection>): string | undefined { export function getEnglishFromRendered(r: T.Rendered<T.NPSelection | T.EqCompSelection | T.APSelection>): string | undefined {
if (r.type === "sandwich") { if (r.selection.type === "sandwich") {
return getEnglishFromRenderedSandwich(r); return getEnglishFromRenderedSandwich(r.selection);
} }
if (!r.e) return undefined; if (!r.selection.e) return undefined;
if (r.type === "loc. adv." || r.type === "adverb") { if (r.selection.type === "loc. adv." || r.selection.type === "adverb") {
return r.e; return r.selection.e;
} }
if (r.type === "adjective") { if (r.selection.type === "adjective") {
return getEnglishFromRenderedAdjective(r); return getEnglishFromRenderedAdjective(r.selection);
} }
if (r.type === "pronoun") { if (r.selection.type === "pronoun") {
return r.e; return r.selection.e;
} }
if (r.type === "participle") { if (r.selection.type === "participle") {
return addPossesors(r.possesor?.np, r.e, r.type); return addPossesors(r.selection.possesor?.np, r.selection.e, r.selection.type);
} }
return addPossesors(r.possesor?.np, addArticlesAndAdjs(r), r.type); return addPossesors(r.selection.possesor?.np, addArticlesAndAdjs(r.selection), r.selection.type);
} }
function getEnglishFromRenderedSandwich(r: T.Rendered<T.SandwichSelection<T.Sandwich>>): string | undefined { function getEnglishFromRenderedSandwich(r: T.Rendered<T.SandwichSelection<T.Sandwich>>): string | undefined {

View File

@ -2,11 +2,15 @@ import * as T from "../../types";
import { renderAdverbSelection } from "./render-ep"; import { renderAdverbSelection } from "./render-ep";
import { renderSandwich } from "./render-sandwich"; import { renderSandwich } from "./render-sandwich";
export function renderAPSelection(ap: T.APSelection): T.Rendered<T.APSelection> { export function renderAPSelection({ selection }: T.APSelection): T.Rendered<T.APSelection> {
if (ap.type === "sandwich") { if (selection.type === "sandwich") {
return renderSandwich(ap); return {
type: "AP",
selection: renderSandwich(selection),
};
} }
// if (ap.type === "adverb") { return {
return renderAdverbSelection(ap); type: "AP",
// } selection: renderAdverbSelection(selection),
};
} }

View File

@ -15,7 +15,7 @@ import { EPSBlocksAreComplete, getSubjectSelection } from "./blocks-utils";
export function renderEP(EP: T.EPSelectionComplete): T.EPRendered { export function renderEP(EP: T.EPSelectionComplete): T.EPRendered {
const subject = getSubjectSelection(EP.blocks).selection; const subject = getSubjectSelection(EP.blocks).selection;
const king = (subject.type === "pronoun") const king = (subject.selection.type === "pronoun")
? "subject" ? "subject"
: EP.predicate.type === "NP" : EP.predicate.type === "NP"
? "predicate" ? "predicate"
@ -27,8 +27,8 @@ export function renderEP(EP: T.EPSelectionComplete): T.EPRendered {
? getPersonFromNP(EP.predicate.selection) ? getPersonFromNP(EP.predicate.selection)
: getPersonFromNP(subject); : getPersonFromNP(subject);
const kingIsParticiple = king === "subject" const kingIsParticiple = king === "subject"
? (subject.type === "participle") ? (subject.selection.type === "participle")
: (EP.predicate.type === "NP" && EP.predicate.selection.type === "participle"); : (EP.predicate.type === "NP" && EP.predicate.selection.selection.type === "participle");
return { return {
type: "EPRendered", type: "EPRendered",
king: EP.predicate.type === "Complement" ? "subject" : "predicate", king: EP.predicate.type === "Complement" ? "subject" : "predicate",
@ -60,11 +60,19 @@ export function getEquativeForm(tense: T.EquativeTense): { hasBa: boolean, form:
function renderEPSBlocks(blocks: T.EPSBlockComplete[], king: "subject" | "predicate"): (T.Rendered<T.SubjectSelectionComplete> | T.Rendered<T.APSelection>)[] { function renderEPSBlocks(blocks: T.EPSBlockComplete[], king: "subject" | "predicate"): (T.Rendered<T.SubjectSelectionComplete> | T.Rendered<T.APSelection>)[] {
return blocks.map(({ block }): (T.Rendered<T.SubjectSelectionComplete> | T.Rendered<T.APSelection>) => { return blocks.map(({ block }): (T.Rendered<T.SubjectSelectionComplete> | T.Rendered<T.APSelection>) => {
if (block.type === "adverb") { if (block.type === "AP") {
return renderAdverbSelection(block); if (block.selection.type === "adverb") {
return {
type: "AP",
selection: renderAdverbSelection(block.selection),
};
} }
if (block.type === "sandwich") { // if (block.selection.type === "sandwich") {
return renderSandwich(block); return {
type: "AP",
selection: renderSandwich(block.selection),
};
// }
} }
return { return {
type: "subjectSelection", type: "subjectSelection",
@ -98,26 +106,35 @@ export function renderAdverbSelection(a: T.AdverbSelection): T.Rendered<T.Adverb
} }
function renderEqCompSelection(s: T.EqCompSelection, person: T.Person): T.Rendered<T.EqCompSelection> { function renderEqCompSelection(s: T.EqCompSelection, person: T.Person): T.Rendered<T.EqCompSelection> {
if (s.type === "sandwich") { if (s.selection.type === "sandwich") {
return renderSandwich(s); return {
type: "EQComp",
selection: renderSandwich(s.selection),
};
} }
const e = getEnglishWord(s.entry); const e = getEnglishWord(s.selection.entry);
if (!e || typeof e !== "string") { if (!e || typeof e !== "string") {
throw new Error("error getting english for compliment"); throw new Error("error getting english for compliment");
} }
if (isLocativeAdverbEntry(s.entry)) { if (isLocativeAdverbEntry(s.selection.entry)) {
return { return {
type: "EQComp",
selection: {
type: "loc. adv.", type: "loc. adv.",
entry: s.entry, entry: s.selection.entry,
ps: [psStringFromEntry(s.entry)], ps: [psStringFromEntry(s.selection.entry)],
e, e,
inflected: false, inflected: false,
person, person,
role: "none", role: "none",
},
}; };
} }
if (s.type === "adjective") { if (s.selection.type === "adjective") {
return renderAdjectiveSelection(s, person, false, "none") return {
type: "EQComp",
selection: renderAdjectiveSelection(s.selection, person, false, "none"),
};
} }
throw new Error("invalid EqCompSelection"); throw new Error("invalid EqCompSelection");
} }

View File

@ -25,14 +25,23 @@ export function renderNPSelection(NP: T.NPSelection | T.ObjectNP, inflected: boo
} }
return NP; return NP;
} }
if (NP.type === "noun") { if (NP.selection.type === "noun") {
return renderNounSelection(NP, inflected, soRole); return {
type: "NP",
selection: renderNounSelection(NP.selection, inflected, soRole),
};
} }
if (NP.type === "pronoun") { if (NP.selection.type === "pronoun") {
return renderPronounSelection(NP, inflected, inflectEnglish, soRole); return {
type: "NP",
selection: renderPronounSelection(NP.selection, inflected, inflectEnglish, soRole),
};
} }
if (NP.type === "participle") { if (NP.selection.type === "participle") {
return renderParticipleSelection(NP, inflected, soRole) return {
type: "NP",
selection: renderParticipleSelection(NP.selection, inflected, soRole),
};
} }
throw new Error("unknown NP type"); throw new Error("unknown NP type");
}; };
@ -91,10 +100,10 @@ function renderParticipleSelection(p: T.ParticipleSelection, inflected: boolean,
function renderPossesor(possesor: T.PossesorSelection | undefined, possesorRole: "servant" | "king" | "none" | "subj/obj"): T.RenderedPossesorSelection | undefined { function renderPossesor(possesor: T.PossesorSelection | undefined, possesorRole: "servant" | "king" | "none" | "subj/obj"): T.RenderedPossesorSelection | undefined {
if (!possesor) return undefined; if (!possesor) return undefined;
const isSingUnisexAnim5PatternNoun = (possesor.np.type === "noun" const isSingUnisexAnim5PatternNoun = (possesor.np.selection.type === "noun"
&& possesor.np.number === "singular" && possesor.np.selection.number === "singular"
&& isAnimNounEntry(possesor.np.entry) && isAnimNounEntry(possesor.np.selection.entry)
&& isPattern5Entry(possesor.np.entry) && isPattern5Entry(possesor.np.selection.entry)
); );
return { return {
shrunken: possesor.shrunken, shrunken: possesor.shrunken,

View File

@ -3,9 +3,9 @@ import { isPattern1Entry, isPattern5Entry, isAnimNounEntry } from "../type-predi
import { renderNPSelection } from "./render-np"; import { renderNPSelection } from "./render-np";
export function renderSandwich(s: T.SandwichSelection<T.Sandwich>): T.Rendered<T.SandwichSelection<T.Sandwich>> { export function renderSandwich(s: T.SandwichSelection<T.Sandwich>): T.Rendered<T.SandwichSelection<T.Sandwich>> {
const inflectInside = (isLocationSandwich(s) && s.inside.type === "noun" && isPattern1Entry(s.inside.entry) && s.inside.number === "singular") const inflectInside = (isLocationSandwich(s) && s.inside.selection.type === "noun" && isPattern1Entry(s.inside.selection.entry) && s.inside.selection.number === "singular")
? false ? false
: (s.inside.type === "noun" && isPattern5Entry(s.inside.entry) && isAnimNounEntry(s.inside.entry)) : (s.inside.selection.type === "noun" && isPattern5Entry(s.inside.selection.entry) && isAnimNounEntry(s.inside.selection.entry))
? false ? false
: true; : true;
return { return {

View File

@ -261,8 +261,8 @@ export function getKingAndServant(isPast: boolean, isTransitive: boolean):
function isFirstOrSecondPersPronoun(o: "none" | T.NPSelection | T.Person.ThirdPlurMale): boolean { function isFirstOrSecondPersPronoun(o: "none" | T.NPSelection | T.Person.ThirdPlurMale): boolean {
if (typeof o !== "object") return false; if (typeof o !== "object") return false;
if (o.type !== "pronoun") return false; if (o.selection.type !== "pronoun") return false;
return [0,1,2,3,6,7,8,9].includes(o.person); return [0,1,2,3,6,7,8,9].includes(o.selection.person);
} }
function isPerfective(t: T.Tense): boolean { function isPerfective(t: T.Tense): boolean {
@ -283,11 +283,11 @@ function isPerfective(t: T.Tense): boolean {
} }
function isMascSingAnimatePattern4(np: T.NPSelection): boolean { function isMascSingAnimatePattern4(np: T.NPSelection): boolean {
if (np.type !== "noun") { if (np.selection.type !== "noun") {
return false; return false;
} }
return isPattern4Entry(np.entry) return isPattern4Entry(np.selection.entry)
&& np.entry.c.includes("anim.") && np.selection.entry.c.includes("anim.")
&& (np.number === "singular") && (np.selection.number === "singular")
&& (np.gender === "masc"); && (np.selection.gender === "masc");
} }

View File

@ -125,15 +125,15 @@ export function getPersonFromNP(np: T.NPSelection | T.ObjectNP): T.Person | unde
return undefined; return undefined;
} }
if (typeof np === "number") return np; if (typeof np === "number") return np;
if (np.type === "participle") { if (np.selection.type === "participle") {
return T.Person.ThirdPlurMale; return T.Person.ThirdPlurMale;
} }
if (np.type === "pronoun") { if (np.selection.type === "pronoun") {
return np.person; return np.selection.person;
} }
return np.number === "plural" return np.selection.number === "plural"
? (np.gender === "masc" ? T.Person.ThirdPlurMale : T.Person.ThirdPlurFemale) ? (np.selection.gender === "masc" ? T.Person.ThirdPlurMale : T.Person.ThirdPlurFemale)
: (np.gender === "masc" ? T.Person.ThirdSingMale : T.Person.ThirdSingFemale); : (np.selection.gender === "masc" ? T.Person.ThirdSingMale : T.Person.ThirdSingFemale);
} }
export function removeBa(ps: T.PsString): T.PsString { export function removeBa(ps: T.PsString): T.PsString {
@ -258,14 +258,17 @@ export function isThirdPerson(p: T.Person): boolean {
export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState): T.VPSelectionState { export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState): T.VPSelectionState {
const subject = getSubjectSelection(vps.blocks).selection; const subject = getSubjectSelection(vps.blocks).selection;
const object = getObjectSelection(vps.blocks).selection; const object = getObjectSelection(vps.blocks).selection;
const subjIs2ndPerson = (subject?.type === "pronoun") && isSecondPerson(subject.person); const subjIs2ndPerson = (subject?.selection.type === "pronoun") && isSecondPerson(subject.selection.person);
const objIs2ndPerson = (typeof object === "object") const objIs2ndPerson = (typeof object === "object")
&& (object.type === "pronoun") && (object.selection.type === "pronoun")
&& isSecondPerson(object.person); && isSecondPerson(object.selection.person);
const default2ndPersSubject: T.PronounSelection = { const default2ndPersSubject: T.NPSelection = {
type: "NP",
selection: {
type: "pronoun", type: "pronoun",
distance: "far", distance: "far",
person: T.Person.SecondSingMale, person: T.Person.SecondSingMale,
},
}; };
function getNon2ndPersPronoun() { function getNon2ndPersPronoun() {
let newObjPerson: T.Person; let newObjPerson: T.Person;
@ -278,22 +281,25 @@ export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState):
return vps; return vps;
} }
if (subjIs2ndPerson && objIs2ndPerson) { if (subjIs2ndPerson && objIs2ndPerson) {
if (typeof object !== "object" || object.type !== "pronoun") { if (typeof object !== "object" || object.selection.type !== "pronoun") {
return vps; return vps;
} }
return { return {
...vps, ...vps,
blocks: adjustObjectSelection(vps.blocks, { blocks: adjustObjectSelection(vps.blocks, {
...object, type: "NP",
selection: {
...object.selection,
person: getNon2ndPersPronoun(), person: getNon2ndPersPronoun(),
},
}), }),
}; };
} }
if (!subjIs2ndPerson && objIs2ndPerson) { if (!subjIs2ndPerson && objIs2ndPerson) {
if (typeof object !== "object" || object.type !== "pronoun") { if (typeof object !== "object" || object.selection.type !== "pronoun") {
return { return {
...vps, ...vps,
blocks: adjustSubjectSelection(vps.blocks, default2ndPersSubject), blocks: adjustSubjectSelection(vps.blocks, default2ndPersSubject)
}; };
} }
return { return {
@ -301,9 +307,12 @@ export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState):
blocks: adjustObjectSelection( blocks: adjustObjectSelection(
adjustSubjectSelection(vps.blocks, default2ndPersSubject), adjustSubjectSelection(vps.blocks, default2ndPersSubject),
{ {
...object, type: "NP",
selection: {
...object.selection,
person: getNon2ndPersPronoun(), person: getNon2ndPersPronoun(),
}, },
},
), ),
}; };
} }

View File

@ -610,9 +610,15 @@ export type VerbObject =
// or intransitive "none" // or intransitive "none"
ObjectNP; ObjectNP;
export type NPSelection = NounSelection | PronounSelection | ParticipleSelection; export type NPSelection = {
type: "NP",
selection: NounSelection | PronounSelection | ParticipleSelection,
};
export type APSelection = AdverbSelection | SandwichSelection<Sandwich>; export type APSelection = {
type: "AP",
selection: AdverbSelection | SandwichSelection<Sandwich>,
};
export type NPType = "noun" | "pronoun" | "participle"; export type NPType = "noun" | "pronoun" | "participle";
@ -679,7 +685,35 @@ export type RenderedPossesorSelection = {
shrunken: boolean, shrunken: boolean,
}; };
export type Rendered<T extends NPSelection | EqCompSelection | AdjectiveSelection | SandwichSelection<Sandwich> | APSelection | SubjectSelectionComplete | ObjectSelectionComplete> = T extends SandwichSelection<Sandwich> export type Rendered<
T extends
| NPSelection
| NPSelection["selection"]
| APSelection
| APSelection["selection"]
| EqCompSelection
| EqCompSelection["selection"]
| SubjectSelectionComplete
| ObjectSelectionComplete
| AdjectiveSelection
| SandwichSelection<Sandwich>
> =
T extends NPSelection
? {
type: "NP",
selection: Rendered<NPSelection["selection"]>
}
: T extends APSelection
? {
type: "AP",
selection: Rendered<APSelection["selection"]>
}
: T extends EqCompSelection
? {
type: "EQComp",
selection: Rendered<EqCompSelection["selection"]>
}
: T extends SandwichSelection<Sandwich>
? Omit<SandwichSelection<Sandwich>, "inside"> & { ? Omit<SandwichSelection<Sandwich>, "inside"> & {
inside: Rendered<NPSelection>, inside: Rendered<NPSelection>,
} }
@ -727,7 +761,6 @@ export type Rendered<T extends NPSelection | EqCompSelection | AdjectiveSelectio
np: Rendered<NPSelection>, np: Rendered<NPSelection>,
}, },
}; };
// TODO: recursive changing this down into the possesor etc.
export type EPSelectionState = { export type EPSelectionState = {
blocks: EPSBlock[], blocks: EPSBlock[],
@ -772,7 +805,10 @@ export type EPSelectionComplete = Omit<EPSelectionState, "predicate" | "blocks">
}; };
export type EqCompType = "adjective" | "loc. adv." | "sandwich" export type EqCompType = "adjective" | "loc. adv." | "sandwich"
export type EqCompSelection = AdjectiveSelection | LocativeAdverbSelection | SandwichSelection<Sandwich>; export type EqCompSelection = {
type: "EQComp",
selection: AdjectiveSelection | LocativeAdverbSelection | SandwichSelection<Sandwich>,
};
export type SandwichSelection<S extends Sandwich> = S & { export type SandwichSelection<S extends Sandwich> = S & {
inside: NPSelection, inside: NPSelection,