added adjectives to Nouns and also fixed the the issue with the nouns not saving the gender/number flexibility. Still need to refactor to keep the flexibility for the VerbSelections - (functions arent saved in useStickyState 🤦‍♂️)

This commit is contained in:
lingdocs 2022-04-27 11:38:43 +05:00
parent 3f9b1161d5
commit 06cd3d2f18
18 changed files with 271 additions and 87 deletions

View File

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

@ -173,18 +173,21 @@ function massageSubjectChange(subject: T.NPSelection | undefined, old: T.EPSelec
} }
if (subject.type === "pronoun" && old.predicate.type === "NP" && old.predicate.NP?.type === "noun" && isUnisexNounEntry(old.predicate.NP.entry)) { if (subject.type === "pronoun" && old.predicate.type === "NP" && old.predicate.NP?.type === "noun" && isUnisexNounEntry(old.predicate.NP.entry)) {
const predicate = old.predicate.NP; const predicate = old.predicate.NP;
const numberAdjusted = predicate.changeNumber const adjusted = {
? predicate.changeNumber(personNumber(subject.person)) ...predicate,
: predicate; ...predicate.numberCanChange ? {
const fullyAdjusted = numberAdjusted.changeGender number: personNumber(subject.person),
? numberAdjusted.changeGender(personGender(subject.person)) } : {},
: numberAdjusted; ...predicate.genderCanChange ? {
gender: personGender(subject.person),
} : {},
}
return { return {
...old, ...old,
subject, subject,
predicate: { predicate: {
...old.predicate, ...old.predicate,
NP: fullyAdjusted, NP: adjusted,
}, },
}; };
} }

View File

@ -0,0 +1,84 @@
import * as T from "../../types";
import { useState } from "react";
import AdjectivePicker from "./AdjectivePicker";
function AdjectiveManager(props: {
adjectives: T.AdjectiveSelection[],
entryFeeder: T.EntryFeederSingleType<T.AdjectiveEntry>,
opts: T.TextOptions,
onChange: (adjs: T.AdjectiveSelection[]) => void,
}) {
const [adding, setAdding] = useState<boolean>(false);
function handleChange(i: number) {
return (a: T.AdjectiveSelection | undefined) => {
if (a === undefined) return;
const updated = [...props.adjectives];
updated[i] = a;
props.onChange(
updated.filter((x): x is T.AdjectiveSelection => !!x)
);
};
}
function deleteAdj(i: number) {
return () => {
props.onChange(remove(props.adjectives, i));
};
}
function handleAddNew(a: T.AdjectiveSelection | undefined) {
if (a === undefined) return;
setAdding(false);
props.onChange([
a,
...props.adjectives,
]);
}
// const flippedList = [...props.adjectives];
// flippedList.reverse();
// console.log(props.adjectives);
return <div className="mb-1">
{!!props.adjectives.length && <div className="d-flex flex-row justify-content-between">
<h6>Adjectives</h6>
{!adding ? <h6 onClick={() => setAdding(true)}>+ Adj.</h6> : <div></div>}
</div>}
{adding && <div>
<div className="d-flex flex-row justify-content-between">
<div>Add Adjective</div>
<div onClick={() => setAdding(false)}>Cancel</div>
</div>
<AdjectivePicker
noTitle
adjective={undefined}
entryFeeder={props.entryFeeder}
opts={props.opts}
onChange={handleAddNew}
/>
</div>}
{props.adjectives.map((adj, i) => (
<div className="d-flex flex-row align-items-baseline">
<AdjectivePicker
noTitle
key={`adj${i}`}
adjective={adj}
entryFeeder={props.entryFeeder}
opts={props.opts}
onChange={handleChange(i)}
/>
<div onClick={deleteAdj(i)} className="ml-4">
<div className="fas fa-trash" />
</div>
</div>
))}
{!adding && !props.adjectives.length && <h6 className="clickable" style={{ float: "right" }}>
<div onClick={() => setAdding(true)}>+ Adj.</div>
</h6>}
</div>;
}
function remove<X>(arr: X[], i: number): X[] {
return [
...arr.slice(0, i),
...arr.slice(i + 1),
];
}
export default AdjectiveManager;

View File

@ -6,6 +6,7 @@ function AdjectivePicker(props: {
adjective: T.AdjectiveSelection | undefined, adjective: T.AdjectiveSelection | undefined,
onChange: (p: T.AdjectiveSelection | undefined) => void, onChange: (p: T.AdjectiveSelection | undefined) => void,
opts: T.TextOptions, opts: T.TextOptions,
noTitle?: boolean,
}) { }) {
function onEntrySelect(entry: T.AdjectiveEntry | undefined) { function onEntrySelect(entry: T.AdjectiveEntry | undefined) {
if (!entry) { if (!entry) {
@ -14,7 +15,7 @@ function AdjectivePicker(props: {
props.onChange(makeAdjectiveSelection(entry)); props.onChange(makeAdjectiveSelection(entry));
} }
return <div style={{ maxWidth: "225px", minWidth: "125px" }}> return <div style={{ maxWidth: "225px", minWidth: "125px" }}>
<h6>Adjective</h6> {!props.noTitle && <h6>Adjective</h6>}
<div> <div>
<EntrySelect <EntrySelect
value={props.adjective?.entry} value={props.adjective?.entry}

View File

@ -4,9 +4,9 @@ import {
import * as T from "../../types"; import * as T from "../../types";
import ButtonSelect from "../ButtonSelect"; import ButtonSelect from "../ButtonSelect";
import InlinePs from "../InlinePs"; import InlinePs from "../InlinePs";
// import { useState } from "react";
// import { isFemNounEntry, isPattern1Entry, isPattern2Entry, isPattern3Entry, isPattern4Entry, isPattern5Entry, isPattern6FemEntry } from "../../lib/type-predicates"; // import { isFemNounEntry, isPattern1Entry, isPattern2Entry, isPattern3Entry, isPattern4Entry, isPattern5Entry, isPattern6FemEntry } from "../../lib/type-predicates";
import EntrySelect from "../EntrySelect"; import EntrySelect from "../EntrySelect";
import AdjectiveManager from "./AdjectiveManager";
// const filterOptions = [ // const filterOptions = [
// { // {
@ -56,7 +56,7 @@ import EntrySelect from "../EntrySelect";
// } // }
function NPNounPicker(props: { function NPNounPicker(props: {
entryFeeder: T.EntryFeederSingleType<T.NounEntry>, entryFeeder: T.EntryFeeder,
noun: T.NounSelection | undefined, noun: T.NounSelection | undefined,
onChange: (p: T.NounSelection | undefined) => void, onChange: (p: T.NounSelection | undefined) => void,
opts: T.TextOptions, opts: T.TextOptions,
@ -76,6 +76,14 @@ function NPNounPicker(props: {
// setPatternFilter(undefined); // setPatternFilter(undefined);
// setShowFilter(false); // setShowFilter(false);
// } // }
function handelAdjectivesUpdate(adjectives: T.AdjectiveSelection[]) {
if (props.noun) {
props.onChange({
...props.noun,
adjectives,
});
}
}
return <div style={{ maxWidth: "225px", minWidth: "125px" }}> return <div style={{ maxWidth: "225px", minWidth: "125px" }}>
{/* {showFilter && <div className="mb-2 text-center"> {/* {showFilter && <div className="mb-2 text-center">
<div className="d-flex flex-row justify-content-between"> <div className="d-flex flex-row justify-content-between">
@ -90,11 +98,17 @@ function NPNounPicker(props: {
handleChange={setPatternFilter} handleChange={setPatternFilter}
/> />
</div>} */} </div>} */}
{props.noun && <AdjectiveManager
adjectives={props.noun?.adjectives}
entryFeeder={props.entryFeeder.adjectives}
opts={props.opts}
onChange={handelAdjectivesUpdate}
/>}
<h6>Noun</h6> <h6>Noun</h6>
{!(props.noun && props.noun.dynamicComplement) ? <div> {!(props.noun && props.noun.dynamicComplement) ? <div>
<EntrySelect <EntrySelect
value={props.noun?.entry} value={props.noun?.entry}
entryFeeder={props.entryFeeder} entryFeeder={props.entryFeeder.nouns}
onChange={onEntrySelect} onChange={onEntrySelect}
name="Noun" name="Noun"
opts={props.opts} opts={props.opts}
@ -112,30 +126,36 @@ function NPNounPicker(props: {
</div>} </div>}
{props.noun && <div className="my-2 d-flex flex-row justify-content-around align-items-center"> {props.noun && <div className="my-2 d-flex flex-row justify-content-around align-items-center">
<div> <div>
{props.noun.changeGender ? <ButtonSelect {props.noun.genderCanChange ? <ButtonSelect
small small
options={[ options={[
{ label: "Masc", value: "masc" }, { label: "Masc", value: "masc" },
{ label: "Fem", value: "fem" }, { label: "Fem", value: "fem" },
]} ]}
value={props.noun.gender} value={props.noun.gender}
handleChange={(g) => { handleChange={(gender) => {
if (!props.noun || !props.noun.changeGender) return; if (!props.noun || !props.noun.genderCanChange) return;
props.onChange(props.noun.changeGender(g)); props.onChange({
...props.noun,
gender,
});
}} }}
/> : props.noun.gender === "masc" ? "Masc." : "Fem."} /> : props.noun.gender === "masc" ? "Masc." : "Fem."}
</div> </div>
<div> <div>
{props.noun.changeNumber ? <ButtonSelect {props.noun.numberCanChange ? <ButtonSelect
small small
options={[ options={[
{ label: "Sing.", value: "singular" }, { label: "Sing.", value: "singular" },
{ label: "Plur.", value: "plural" }, { label: "Plur.", value: "plural" },
]} ]}
value={props.noun.number} value={props.noun.number}
handleChange={(n) => { handleChange={(number) => {
if (!props.noun || !props.noun.changeNumber) return; if (!props.noun || !props.noun.numberCanChange) return;
props.onChange(props.noun.changeNumber(n)); props.onChange({
...props.noun,
number,
});
}} }}
/> : props.noun.number === "singular" ? "Sing." : "Plur."} /> : props.noun.number === "singular" ? "Sing." : "Plur."}
</div> </div>

View File

@ -93,7 +93,7 @@ function NPPicker(props: {
/> />
: npType === "noun" : npType === "noun"
? <NounPicker ? <NounPicker
entryFeeder={props.entryFeeder.nouns} entryFeeder={props.entryFeeder}
noun={(props.np && props.np.type === "noun") ? props.np : undefined} noun={(props.np && props.np.type === "noun") ? props.np : undefined}
onChange={props.onChange} onChange={props.onChange}
opts={props.opts} opts={props.opts}

View File

@ -102,23 +102,10 @@ export function makeNounSelection(entry: T.NounEntry, dynamicComplement?: true):
type: "noun", type: "noun",
entry, entry,
gender: isMascNounEntry(entry) ? "masc" : "fem", gender: isMascNounEntry(entry) ? "masc" : "fem",
genderCanChange: isUnisexNounEntry(entry),
number, number,
numberCanChange: number === "singular",
adjectives: [],
dynamicComplement, dynamicComplement,
...isUnisexNounEntry(entry) ? {
changeGender: function(gender: T.Gender): T.NounSelection {
return {
...this,
gender,
};
},
} : {},
...number === "singular" ? {
changeNumber: function(number: T.NounNumber): T.NounSelection {
return {
...this,
number,
};
},
} : {},
}; };
} }

View File

@ -68,7 +68,7 @@ function CompoundDisplay({ info, opts, handleLinkClick }: {
<div className="text-center">{info.type}</div> <div className="text-center">{info.type}</div>
<CompoundFormula <CompoundFormula
a={<div a={<div
className={classNames([{ clickable: handleLinkClick }])} className={classNames([{ clickable: typeof handleLinkClick === "function" }])}
onClick={(handleLinkClick) onClick={(handleLinkClick)
// @ts-ignore - thinks there might not be a complement, but there will be // @ts-ignore - thinks there might not be a complement, but there will be
? () => handleLinkClick(info.entry.complement?.ts) ? () => handleLinkClick(info.entry.complement?.ts)

View File

@ -11,10 +11,11 @@ import {
flattenLengths, flattenLengths,
} from "./segment"; } from "./segment";
import { removeAccents } from "../accent-helpers"; import { removeAccents } from "../accent-helpers";
import { getEnglishFromRendered, getPashtoFromRendered } from "./np-tools";
export function compileEP(EP: T.EPRendered, form: T.FormVersion): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] }; export function compileEP(EP: T.EPRendered, form: T.FormVersion): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string[] };
export function compileEP(EP: T.EPRendered, form: T.FormVersion, combineLengths: true): { ps: T.PsString[], e?: string [] }; export function compileEP(EP: T.EPRendered, form: T.FormVersion, combineLengths: true): { ps: T.PsString[], e?: string[] };
export function compileEP(EP: T.EPRendered, form: T.FormVersion, combineLengths?: true): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] } { export function compileEP(EP: T.EPRendered, form: T.FormVersion, combineLengths?: true): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string[] } {
const { kids, NPs } = getSegmentsAndKids(EP, form); const { kids, NPs } = getSegmentsAndKids(EP, form);
const equative = EP.equative.ps; const equative = EP.equative.ps;
const psResult = compilePs({ const psResult = compilePs({
@ -36,8 +37,8 @@ function getSegmentsAndKids(EP: T.EPRendered, form: T.FormVersion): { kids: Segm
} }
return [s]; return [s];
} }
const subject = makeSegment(EP.subject.ps); const subject = makeSegment(getPashtoFromRendered(EP.subject));
const predicate = makeSegment(EP.predicate.ps); const predicate = makeSegment(getPashtoFromRendered(EP.predicate));
return { return {
kids: EP.equative.hasBa kids: EP.equative.hasBa
? [makeSegment(grammarUnits.baParticle, ["isBa", "isKid"])] ? [makeSegment(grammarUnits.baParticle, ["isBa", "isKid"])]
@ -77,8 +78,8 @@ function compileEnglish(EP: T.EPRendered): string[] | undefined {
function insertEWords(e: string, { subject, predicate }: { subject: string, predicate: string }): string { function insertEWords(e: string, { subject, predicate }: { subject: string, predicate: string }): string {
return e.replace("$SUBJ", subject).replace("$PRED", predicate || ""); return e.replace("$SUBJ", subject).replace("$PRED", predicate || "");
} }
const engSubj = EP.subject.e || undefined; const engSubj = getEnglishFromRendered(EP.subject);
const engPred = EP.predicate.e || undefined; const engPred = getEnglishFromRendered(EP.predicate);
// require all English parts for making the English phrase // require all English parts for making the English phrase
return (EP.englishBase && engSubj && engPred) return (EP.englishBase && engSubj && engPred)
? EP.englishBase.map(e => insertEWords(e, { ? EP.englishBase.map(e => insertEWords(e, {

View File

@ -20,6 +20,7 @@ import {
removeDuplicates, removeDuplicates,
} from "./vp-tools"; } from "./vp-tools";
import { isImperativeTense, isModalTense, isPerfectTense } from "../type-predicates"; import { isImperativeTense, isModalTense, isPerfectTense } from "../type-predicates";
import { getEnglishFromRendered, getPashtoFromRendered } from "./np-tools";
type Form = T.FormVersion & { OSV?: boolean }; type Form = T.FormVersion & { OSV?: boolean };
export function compileVP(VP: T.VPRendered, form: Form): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] }; export function compileVP(VP: T.VPRendered, form: Form): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] };
@ -79,8 +80,8 @@ function compilePs({ NPs, kids, verb: { head, rest }, VP }: CompilePsInput): T.S
function getSegmentsAndKids(VP: T.VPRendered, form: Form): { kids: Segment[], NPs: Segment[][] } { function getSegmentsAndKids(VP: T.VPRendered, form: Form): { kids: Segment[], NPs: Segment[][] } {
const SO = { const SO = {
subject: VP.subject.ps, subject: getPashtoFromRendered(VP.subject),
object: typeof VP.object === "object" ? VP.object.ps : undefined, object: typeof VP.object === "object" ? getPashtoFromRendered(VP.object) : undefined,
} }
const removeKing = form.removeKing && !(VP.isCompound === "dynamic" && VP.isPast); const removeKing = form.removeKing && !(VP.isCompound === "dynamic" && VP.isPast);
const shrinkServant = form.shrinkServant && !(VP.isCompound === "dynamic" && !VP.isPast); const shrinkServant = form.shrinkServant && !(VP.isCompound === "dynamic" && !VP.isPast);
@ -282,8 +283,10 @@ function compileEnglish(VP: T.VPRendered): string[] | undefined {
function insertEWords(e: string, { subject, object }: { subject: string, object?: string }): string { function insertEWords(e: string, { subject, object }: { subject: string, object?: string }): string {
return e.replace("$SUBJ", subject).replace("$OBJ", object || ""); return e.replace("$SUBJ", subject).replace("$OBJ", object || "");
} }
const engSubj = VP.subject.e || undefined; const engSubj = getEnglishFromRendered(VP.subject);
const engObj = (typeof VP.object === "object" && VP.object.e) ? VP.object.e : undefined; const engObj = typeof VP.object === "object"
? getEnglishFromRendered(VP.object)
: undefined;
// 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 || typeof VP.object !== "object"))
? VP.englishBase.map(e => insertEWords(e, { ? VP.englishBase.map(e => insertEWords(e, {

View File

@ -0,0 +1,42 @@
import * as T from "../../types";
import { concatPsString } from "../p-text-helpers";
export function getPashtoFromRendered(np: T.Rendered<T.NPSelection | T.EqCompSelection>): T.PsString[] {
const adjs = np.adjectives;
if (!adjs) {
return np.ps;
}
return np.ps.map(p => (
concatPsString(
adjs.reduce((accum, curr) => (
// TODO: with variations of adjs?
concatPsString(accum, " ", curr.ps[0])
), { p: "", f: "" }),
" ",
p,
)
));
}
export function getEnglishFromRendered(np: T.Rendered<T.NPSelection | T.EqCompSelection>): string | undefined {
if (!np.e) return undefined;
if (np.type === "noun") {
try {
// split out the atricles so adjectives can be stuck inbetween them and the word
const chunks = np.e.split("the)");
const [articles, word] = chunks.length === 1
? ["", np.e]
: [chunks[0] + "the) ", chunks[1]];
const adjs = !np.adjectives
? ""
: np.adjectives.reduce((accum, curr): string => {
if (!curr.e) throw new Error("no english for adjective");
return accum + curr.e + " ";
}, "");
return `${articles}${adjs}${word}`;
} catch (e) {
return undefined;
}
}
return np.e;
}

View File

@ -0,0 +1,47 @@
import * as T from "../../types";
import { inflectWord } from "../../lib/pashto-inflector";
import {
psStringFromEntry,
isUnisexSet,
} from "../../lib/p-text-helpers";
import { getEnglishWord } from "../get-english-word";
import {
personGender,
personIsPlural,
} from "../../lib/misc-helpers";
function chooseInflection(inflections: T.UnisexSet<T.InflectionSet>, pers: T.Person, inflected?: boolean): T.ArrayOneOrMore<T.PsString> {
const gender = personGender(pers);
const plural = personIsPlural(pers);
const infNumber = (plural ? 1 : 0) + (inflected ? 1 : 0);
return inflections[gender][infNumber];
}
export function renderAdjectiveSelection(a: T.AdjectiveSelection, person: T.Person, inflected: boolean): T.Rendered<T.AdjectiveSelection> {
const infs = inflectWord(a.entry);
const eWord = getEnglishWord(a.entry);
const e = !eWord
? undefined
: typeof eWord === "string"
? eWord
: (eWord.singular || undefined);
if (!infs) return {
type: "adjective",
entry: a.entry,
ps: [psStringFromEntry(a.entry)],
e,
inflected: false,
person,
}
if (!infs.inflections || !isUnisexSet(infs.inflections)) {
throw new Error("error getting inflections for adjective, looks like a noun's inflections");
}
return {
type: "adjective",
entry: a.entry,
ps: chooseInflection(infs.inflections, person, inflected),
e,
inflected: false,
person,
};
}

View File

@ -7,10 +7,10 @@ import { renderNPSelection } from "./render-np";
import { getPersonFromVerbForm } from "../../lib/misc-helpers"; import { getPersonFromVerbForm } from "../../lib/misc-helpers";
import { getVerbBlockPosFromPerson } from "../misc-helpers"; import { getVerbBlockPosFromPerson } from "../misc-helpers";
import { getEnglishWord } from "../get-english-word"; import { getEnglishWord } from "../get-english-word";
import { isUnisexSet, psStringFromEntry } from "../p-text-helpers"; import { psStringFromEntry } from "../p-text-helpers";
import { inflectWord } from "../pashto-inflector";
import { personGender, personIsPlural } from "../../library"; import { personGender, personIsPlural } from "../../library";
import { isLocativeAdverbEntry } from "../type-predicates"; import { isLocativeAdverbEntry } from "../type-predicates";
import { renderAdjectiveSelection } from "./render-adj";
export function renderEP(EP: T.EPSelectionComplete): T.EPRendered { export function renderEP(EP: T.EPSelectionComplete): T.EPRendered {
const kingPerson = (EP.subject.type === "pronoun") const kingPerson = (EP.subject.type === "pronoun")
@ -71,26 +71,7 @@ function renderEqCompSelection(s: T.EqCompSelection, person: T.Person): T.Render
}; };
} }
if (s.type === "adjective") { if (s.type === "adjective") {
const infs = inflectWord(s.entry); return renderAdjectiveSelection(s, person, false)
if (!infs) return {
type: "adjective",
entry: s.entry,
ps: [psStringFromEntry(s.entry)],
e,
inflected: false,
person,
}
if (!infs.inflections || !isUnisexSet(infs.inflections)) {
throw new Error("error getting inflections for adjective, looks like a noun's inflections");
}
return {
type: "adjective",
entry: s.entry,
ps: chooseInflection(infs.inflections, person),
e,
inflected: false,
person,
};
} }
throw new Error("invalid EqCompSelection"); throw new Error("invalid EqCompSelection");
} }

View File

@ -11,6 +11,7 @@ import {
} from "../p-text-helpers"; } from "../p-text-helpers";
import { parseEc } from "../misc-helpers"; import { parseEc } from "../misc-helpers";
import { getEnglishWord } from "../get-english-word"; import { getEnglishWord } from "../get-english-word";
import { renderAdjectiveSelection } from "./render-adj";
export function renderNPSelection(NP: T.NPSelection, inflected: boolean, inflectEnglish: boolean, role: "subject"): T.Rendered<T.NPSelection> export function renderNPSelection(NP: T.NPSelection, inflected: boolean, inflectEnglish: boolean, role: "subject"): T.Rendered<T.NPSelection>
export function renderNPSelection(NP: T.NPSelection | T.ObjectNP, inflected: boolean, inflectEnglish: boolean, role: "object"): T.Rendered<T.NPSelection> | T.Person.ThirdPlurMale | "none"; export function renderNPSelection(NP: T.NPSelection | T.ObjectNP, inflected: boolean, inflectEnglish: boolean, role: "object"): T.Rendered<T.NPSelection> | T.Person.ThirdPlurMale | "none";
@ -48,9 +49,11 @@ function renderNounSelection(n: T.NounSelection, inflected: boolean): T.Rendered
? ps ? ps
: [psStringFromEntry(n.entry)]; : [psStringFromEntry(n.entry)];
})(); })();
const person = getPersonNumber(n.gender, n.number);
return { return {
...n, ...n,
person: getPersonNumber(n.gender, n.number), adjectives: n.adjectives.map(a => renderAdjectiveSelection(a, person, inflected)),
person,
inflected, inflected,
ps: pashto, ps: pashto,
e: english, e: english,

View File

@ -43,7 +43,7 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
const inflectSubject = isPast && isTransitive && !isMascSingAnimatePattern4(VP.subject); const inflectSubject = isPast && isTransitive && !isMascSingAnimatePattern4(VP.subject);
const inflectObject = !isPast && isFirstOrSecondPersPronoun(VP.verb.object); const inflectObject = !isPast && isFirstOrSecondPersPronoun(VP.verb.object);
// Render Elements // Render Elements
return { const b: T.VPRendered = {
type: "VPRendered", type: "VPRendered",
king, king,
servant, servant,
@ -59,6 +59,7 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
vs: VP.verb, vs: VP.verb,
}), }),
}; };
return b;
} }
function renderVerbSelection(vs: T.VerbSelectionComplete, person: T.Person, objectPerson: T.Person | undefined): T.VerbRendered { function renderVerbSelection(vs: T.VerbSelectionComplete, person: T.Person, objectPerson: T.Person | undefined): T.VerbRendered {

View File

@ -25,10 +25,23 @@ export type Segment = { ps: T.PsString[] } & SegmentDescriptions & {
adjust: (o: { ps?: T.PsString | T.PsString[] | ((ps: T.PsString) => T.PsString), desc?: SDT[] }) => Segment, adjust: (o: { ps?: T.PsString | T.PsString[] | ((ps: T.PsString) => T.PsString), desc?: SDT[] }) => Segment,
}; };
function addAdjectives(ps: T.PsString[], adjectives?: T.Rendered<T.AdjectiveSelection>[]): T.PsString[] {
if (!adjectives) return ps;
return ps.map(p => {
// TODO: more thorough use of all possible variends?
return concatPsString(...adjectives.map(a => a.ps[0]), p);
});
}
export function makeSegment( export function makeSegment(
ps: T.PsString | T.PsString[], input: T.Rendered<T.NounSelection> | T.PsString | T.PsString[],
options?: (keyof SegmentDescriptions)[], options?: (keyof SegmentDescriptions)[],
): Segment { ): Segment {
const ps: T.PsString[] = Array.isArray(input)
? input
: "type" in input
? addAdjectives(input.ps, input.adjectives)
: [input];
return { return {
ps: Array.isArray(ps) ? ps : [ps], ps: Array.isArray(ps) ? ps : [ps],
...options && options.reduce((all, curr) => ({ ...options && options.reduce((all, curr) => ({
@ -39,6 +52,7 @@ export function makeSegment(
return { return {
...this, ...this,
...o.ps ? { ...o.ps ? {
// TODO: is this ok with the adjectives?
ps: Array.isArray(o.ps) ps: Array.isArray(o.ps)
? o.ps ? o.ps
: "p" in o.ps : "p" in o.ps

View File

@ -592,16 +592,11 @@ export type NounSelection = {
type: "noun", type: "noun",
entry: NounEntry, entry: NounEntry,
gender: Gender, gender: Gender,
genderCanChange: boolean,
number: NounNumber, number: NounNumber,
numberCanChange: boolean,
dynamicComplement?: boolean, dynamicComplement?: boolean,
// TODO: Implement adjectives: AdjectiveSelection[],
// adjectives: [],
// TODO: Implement
// possesor: NPSelection | undefined,
/* method only present if it's possible to change gender */
changeGender?: (gender: Gender) => NounSelection,
/* method only present if it's possible to change number */
changeNumber?: (number: NounNumber) => NounSelection,
}; };
export type AdjectiveSelection = { export type AdjectiveSelection = {
@ -633,8 +628,8 @@ export type ReplaceKey<T, K extends string, R> = T extends Record<K, unknown> ?
export type FormVersion = { removeKing: boolean, shrinkServant: boolean }; export type FormVersion = { removeKing: boolean, shrinkServant: boolean };
export type Rendered<T extends NPSelection | EqCompSelection> = ReplaceKey< export type Rendered<T extends NPSelection | EqCompSelection | AdjectiveSelection> = ReplaceKey<
Omit<T, "changeGender" | "changeNumber" | "changeDistance">, Omit<T, "changeGender" | "changeNumber" | "changeDistance" | "adjectives">,
"e", "e",
string string
> & { > & {
@ -642,6 +637,8 @@ export type Rendered<T extends NPSelection | EqCompSelection> = ReplaceKey<
e?: string, e?: string,
inflected: boolean, inflected: boolean,
person: Person, person: Person,
// TODO: better recursive thing
adjectives?: Rendered<AdjectiveSelection>[],
}; };
// TODO: recursive changing this down into the possesor etc. // TODO: recursive changing this down into the possesor etc.