UGLY basic ability to shrink servants

This commit is contained in:
lingdocs 2022-03-17 16:37:24 +04:00
parent 06fa7966f8
commit 5c510a6128
6 changed files with 157 additions and 77 deletions

View File

@ -8,12 +8,13 @@ import {
isInvalidSubjObjCombo,
} from "../../lib/np-tools";
function verbPhraseComplete({ subject, verb }: { subject: NPSelection | undefined, verb: VerbSelection | undefined }): VPSelection | undefined {
function verbPhraseComplete({ subject, verb, shrinkServant }: { subject: NPSelection | undefined, verb: VerbSelection | undefined, shrinkServant: boolean }): VPSelection | undefined {
if (!subject) return undefined;
if (!verb) return undefined;
if (verb.object === undefined) return undefined;
return {
type: "VPSelection",
shrinkServant,
subject,
object: verb.object,
verb,
@ -23,6 +24,7 @@ function verbPhraseComplete({ subject, verb }: { subject: NPSelection | undefine
export function PhraseBuilder() {
const [subject, setSubject] = useState<NPSelection | undefined>(undefined);
const [verb, setVerb] = useState<VerbSelection | undefined>(undefined);
const [shrinkServant, setShrinkServant] = useState<boolean>(false);
function handleSubjectChange(subject: NPSelection | undefined) {
const objPronoun = (typeof verb?.object === "object" && verb.object.type === "pronoun")
? verb.object.person
@ -55,7 +57,10 @@ export function PhraseBuilder() {
object,
});
}
const verbPhrase: VPSelection | undefined = verbPhraseComplete({ subject, verb });
function handleShrinkServantChange(e: React.ChangeEvent<HTMLInputElement>) {
setShrinkServant(e.target.checked);
}
const verbPhrase: VPSelection | undefined = verbPhraseComplete({ subject, verb, shrinkServant });
return <div>
<div className="d-flex flex-row justify-content-between">
<div className="mr-2">
@ -75,6 +80,17 @@ export function PhraseBuilder() {
<VerbPicker verbs={verbs} verb={verb} onChange={setVerb} />
</div>
</div>
{/* TODO: make this appear conditionally */}
{(verbPhrase?.object && typeof verbPhrase.object === "object") && <div className="form-group form-check">
<input
className="form-check-input"
name="shrinkServant"
type="checkbox"
checked={shrinkServant}
onChange={handleShrinkServantChange}
/>
<label className="form-check-label">Shrink servant</label>
</div>}
{verbPhrase && <div>
<VPDisplay VP={verbPhrase} />
</div>}

View File

@ -5,6 +5,7 @@ import {
} from "@lingdocs/pashto-inflector";
function VPDisplay({ VP }: { VP: VPSelection }) {
// TODO: Possibly put the servant shrinking in here after the render
const result = compileVP(renderVP(VP));
const rPs = "long" in result.ps ? result.ps.long : result.ps;
return <div className="text-center mt-2">

View File

@ -2,97 +2,136 @@ import {
Types as T,
concatPsString,
removeAccents,
grammarUnits,
getVerbBlockPosFromPerson,
} from "@lingdocs/pashto-inflector";
type ListOfEntities = T.PsString[][];
export function compileVP(VP: VPRendered): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] } {
console.log(VP);
const { head, rest } = VP.verb.ps;
const subj = VP.subject.ps;
const obj = typeof VP.object === "object" ? VP.object.ps : undefined;
// better: feed in array of NPs [subj, obj, etc...]
const { kids, NPs } = shrinkEntitiesAndGatherKids(VP);
return {
ps: arrangePs(subj, obj, head, rest, VP.verb.negative),
ps: compilePs(NPs, head, rest, VP.verb.negative, kids),
e: compileEnglish(VP),
};
}
function arrangePs(
subj: T.PsString[],
obj: T.PsString[] | undefined,
function compilePs(
nps: ListOfEntities,
head: T.PsString | undefined,
rest: T.PsString[],
negative: boolean,
kids: ListOfEntities,
): T.PsString[];
function arrangePs(
subj: T.PsString[],
obj: T.PsString[] | undefined,
function compilePs(
nps: ListOfEntities,
head: T.PsString | undefined,
rest: T.SingleOrLengthOpts<T.PsString[]>,
negative: boolean,
kids: ListOfEntities,
): T.SingleOrLengthOpts<T.PsString[]>;
function arrangePs(
subj: T.PsString[],
obj: T.PsString[] | undefined,
function compilePs(
nps: ListOfEntities,
head: T.PsString | undefined,
rest: T.SingleOrLengthOpts<T.PsString[]>,
negative: boolean,
kids: ListOfEntities,
): T.SingleOrLengthOpts<T.PsString[]> {
if ("long" in rest) {
return {
long: arrangePs(subj, obj, head, rest.long, negative),
short: arrangePs(subj, obj, head, rest.short, negative),
long: compilePs(nps, head, rest.long, negative, kids),
short: compilePs(nps, head, rest.short, negative, kids),
...rest.mini ? {
mini: arrangePs(subj, obj, head, rest.mini, negative),
mini: compilePs(nps, head, rest.mini, negative, kids),
} : {},
};
}
const verbWNeg = arrangeVerbWNeg(head, rest, negative);
if (obj) {
return subj.flatMap(s => (
obj.flatMap(o =>
verbWNeg.flatMap(v => (
concatPsString(s, " ", o, " ", v)
// concatPsString(o, " ", s, " ", v),
))
))
);
const entities: ListOfEntities = [
...nps,
...compileVerbWNegative(head, rest, negative)
];
const entitiesWKids = putKidsInKidsSection(entities, kids);
return combineEntities(entitiesWKids);
}
function shrinkEntitiesAndGatherKids(VP: VPRendered): { kids: ListOfEntities, NPs: ListOfEntities } {
const main = {
subject: VP.subject.ps,
object: typeof VP.object === "object" ? VP.object.ps : undefined,
}
return subj.flatMap(s => (
verbWNeg.flatMap(v => (
concatPsString(s, " ", v)
const toShrink = (VP.shrinkServant && VP.servant)
? VP[VP.servant]
: undefined;
if (!toShrink || typeof toShrink !== "object") {
return {
kids: [],
NPs: [main.subject, ...main.object ? [main.object] : []]
};
}
const king = main[VP.king];
if (!king) {
throw new Error("lost the king in the shrinking process");
}
return {
kids: [shrink(toShrink)],
NPs: [],
}
}
function shrink(np: Rendered<NPSelection>): T.PsString[] {
const person: T.Person = np.type === "participle"
? T.Person.ThirdPlurMale
// @ts-ignore
: np.person;
const [row, col] = getVerbBlockPosFromPerson(person);
return grammarUnits.pronouns.mini[row][col];
}
function putKidsInKidsSection(entities: ListOfEntities, kids: ListOfEntities): ListOfEntities {
const first = entities[0];
const rest = entities.slice(1);
return [
first,
...kids,
...rest,
];
}
function combineEntities(loe: ListOfEntities): T.PsString[] {
const first = loe[0];
const rest = loe.slice(1);
if (!rest.length) return first;
return combineEntities(rest).flatMap(r => (
first.map(ps => concatPsString(
ps,
ps.p === "و" ? { p: "", f: "-" } : " ",
r,
))
));
}
function arrangeVerbWNeg(head: T.PsString | undefined, rest: T.PsString[], negative: boolean): T.PsString[] {
if (!negative) {
return rest.map(ps => concatPsString(head || "", ps));
}
function compileVerbWNegative(head: T.PsString | undefined, rest: T.PsString[], negative: boolean): ListOfEntities {
if (!negative) {
return [
...head ? [[head]] : [],
rest,
];
}
const nu: T.PsString = { p: "نه", f: "nú" };
if (!head) {
return rest.map(r => concatPsString(nu, " ", removeAccents(r)));
return [
[nu],
rest.map(r => removeAccents(r)),
];
}
const regularPrefix = head.p === "و" || head.p === "وا";
const withNuAfterHead = rest.map(r => concatPsString(
removeAccents(head),
{ p: "", f: "-" },
nu,
" ",
removeAccents(r),
));
if (regularPrefix) {
return withNuAfterHead;
}
const withNuBeforeHead = rest.map(r => concatPsString(
nu,
" ",
removeAccents(head),
removeAccents(r),
));
// const regularPrefix = head.p === "و" || head.p === "وا";
// if (regularPrefix) {
// dashes for oo-nu etc
return [
...withNuAfterHead,
...withNuBeforeHead,
[removeAccents(head)],
rest.map(r => concatPsString(nu, " ", removeAccents(r)))
];
}

View File

@ -12,12 +12,16 @@ import {
import {
psStringFromEntry,
} from "../text-tools";
import {
getPersonFromNP,
} from "./vp-tools";
import { isPattern4Entry } from "../type-predicates";
export function renderVP(VP: VPSelection): VPRendered {
// Sentence Rules Logic
const isPast = isPastTense(VP.verb.tense);
const isTransitive = VP.object !== "none";
const { king, /* servant */ } = getKingAndServant(isPast, isTransitive);
const { king, servant } = getKingAndServant(isPast, isTransitive);
const kingPerson = getPersonFromNP(VP[king]);
// TODO: more elegant way of handling this type safety
if (kingPerson === undefined) {
@ -26,11 +30,16 @@ export function renderVP(VP: VPSelection): VPRendered {
const subjectPerson = getPersonFromNP(VP.subject);
const objectPerson = getPersonFromNP(VP.object);
// TODO: also don't inflect if it's a pattern one animate noun
const inflectSubject = isPast && isTransitive;
const inflectSubject = isPast && isTransitive && !isSingularAnimatePattern4(VP.subject);
const inflectObject = !isPast && isFirstOrSecondPersPronoun(VP.object);
// Render Elements
return {
type: "VPRendered",
king,
servant,
isPast,
isTransitive,
shrinkServant: VP.shrinkServant,
subject: renderNPSelection(VP.subject, inflectSubject, false, "subject"),
object: renderNPSelection(VP.object, inflectObject, true, "object"),
verb: renderVerbSelection(VP.verb, kingPerson, objectPerson),
@ -292,24 +301,6 @@ function getTenseVerbForm(conj: T.VerbConjugation, tense: VerbTense): T.VerbForm
throw new Error("unknown tense");
}
function getPersonFromNP(np: NPSelection): T.Person;
function getPersonFromNP(np: NPSelection | ObjectNP): T.Person | undefined;
function getPersonFromNP(np: NPSelection | ObjectNP): T.Person | undefined {
if (np === "none") {
return undefined;
}
if (typeof np === "number") return np;
if (np.type === "participle") {
return T.Person.ThirdPlurMale;
}
if (np.type === "pronoun") {
return np.person;
}
return np.number === "plural"
? (np.gender === "masc" ? T.Person.ThirdPlurMale : T.Person.ThirdPlurFemale)
: (np.gender === "masc" ? T.Person.ThirdSingMale : T.Person.ThirdSingFemale);
}
function getEnglishParticiple(entry: T.DictionaryEntry): string {
if (!entry.ec) {
console.log("errored participle");
@ -349,7 +340,6 @@ function getInf(infs: T.InflectorOutput, t: "plural" | "arabicPlural" | "inflect
// @ts-ignore
const iset = infs[t][gender] as T.InflectionSet;
const inflectionNumber = (inflected ? 1 : 0) + ((t === "inflections" && plural) ? 1 : 0);
console.log({ t, plural, inflectionNumber });
return iset[inflectionNumber];
}
return [];
@ -390,3 +380,10 @@ function isPerfective(t: VerbTense): boolean {
}
throw new Error("tense not implemented yet");
}
function isSingularAnimatePattern4(np: NPSelection): boolean {
if (np.type !== "noun") {
return false;
}
return isPattern4Entry(np.entry) && np.entry.c.includes("anim.") && (np.number === "singular");
}

View File

@ -0,0 +1,21 @@
import {
Types as T,
} from "@lingdocs/pashto-inflector";
export function getPersonFromNP(np: NPSelection): T.Person;
export function getPersonFromNP(np: NPSelection | ObjectNP): T.Person | undefined;
export function getPersonFromNP(np: NPSelection | ObjectNP): T.Person | undefined {
if (np === "none") {
return undefined;
}
if (typeof np === "number") return np;
if (np.type === "participle") {
return T.Person.ThirdPlurMale;
}
if (np.type === "pronoun") {
return np.person;
}
return np.number === "plural"
? (np.gender === "masc" ? T.Person.ThirdPlurMale : T.Person.ThirdPlurFemale)
: (np.gender === "masc" ? T.Person.ThirdSingMale : T.Person.ThirdSingFemale);
}

View File

@ -10,11 +10,17 @@ type VPSelection = {
subject: NPSelection,
object: Exclude<VerbObject, undefined>,
verb: Exclude<VerbSelection, "object">,
shrinkServant: boolean,
};
// TODO: make this Rendered<VPSelection> with recursive Rendered<>
type VPRendered = {
type: "VPRendered",
king: "subject" | "object",
servant: "subject" | "object" | undefined,
shrinkServant: boolean,
isPast: boolean,
isTransitive: boolean,
subject: Rendered<NPSelection>,
object: Rendered<NPSelection> | ObjectNP,
verb: VerbRendered,