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, isInvalidSubjObjCombo,
} from "../../lib/np-tools"; } 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 (!subject) return undefined;
if (!verb) return undefined; if (!verb) return undefined;
if (verb.object === undefined) return undefined; if (verb.object === undefined) return undefined;
return { return {
type: "VPSelection", type: "VPSelection",
shrinkServant,
subject, subject,
object: verb.object, object: verb.object,
verb, verb,
@ -23,6 +24,7 @@ function verbPhraseComplete({ subject, verb }: { subject: NPSelection | undefine
export function PhraseBuilder() { export function PhraseBuilder() {
const [subject, setSubject] = useState<NPSelection | undefined>(undefined); const [subject, setSubject] = useState<NPSelection | undefined>(undefined);
const [verb, setVerb] = useState<VerbSelection | undefined>(undefined); const [verb, setVerb] = useState<VerbSelection | undefined>(undefined);
const [shrinkServant, setShrinkServant] = useState<boolean>(false);
function handleSubjectChange(subject: NPSelection | undefined) { function handleSubjectChange(subject: NPSelection | undefined) {
const objPronoun = (typeof verb?.object === "object" && verb.object.type === "pronoun") const objPronoun = (typeof verb?.object === "object" && verb.object.type === "pronoun")
? verb.object.person ? verb.object.person
@ -55,7 +57,10 @@ export function PhraseBuilder() {
object, 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> return <div>
<div className="d-flex flex-row justify-content-between"> <div className="d-flex flex-row justify-content-between">
<div className="mr-2"> <div className="mr-2">
@ -75,6 +80,17 @@ export function PhraseBuilder() {
<VerbPicker verbs={verbs} verb={verb} onChange={setVerb} /> <VerbPicker verbs={verbs} verb={verb} onChange={setVerb} />
</div> </div>
</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> {verbPhrase && <div>
<VPDisplay VP={verbPhrase} /> <VPDisplay VP={verbPhrase} />
</div>} </div>}

View File

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

View File

@ -2,97 +2,136 @@ import {
Types as T, Types as T,
concatPsString, concatPsString,
removeAccents, removeAccents,
grammarUnits,
getVerbBlockPosFromPerson,
} from "@lingdocs/pashto-inflector"; } from "@lingdocs/pashto-inflector";
type ListOfEntities = T.PsString[][];
export function compileVP(VP: VPRendered): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] } { export function compileVP(VP: VPRendered): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] } {
console.log(VP);
const { head, rest } = VP.verb.ps; const { head, rest } = VP.verb.ps;
const subj = VP.subject.ps; const { kids, NPs } = shrinkEntitiesAndGatherKids(VP);
const obj = typeof VP.object === "object" ? VP.object.ps : undefined;
// better: feed in array of NPs [subj, obj, etc...]
return { return {
ps: arrangePs(subj, obj, head, rest, VP.verb.negative), ps: compilePs(NPs, head, rest, VP.verb.negative, kids),
e: compileEnglish(VP), e: compileEnglish(VP),
}; };
} }
function arrangePs( function compilePs(
subj: T.PsString[], nps: ListOfEntities,
obj: T.PsString[] | undefined,
head: T.PsString | undefined, head: T.PsString | undefined,
rest: T.PsString[], rest: T.PsString[],
negative: boolean, negative: boolean,
kids: ListOfEntities,
): T.PsString[]; ): T.PsString[];
function arrangePs( function compilePs(
subj: T.PsString[], nps: ListOfEntities,
obj: T.PsString[] | undefined,
head: T.PsString | undefined, head: T.PsString | undefined,
rest: T.SingleOrLengthOpts<T.PsString[]>, rest: T.SingleOrLengthOpts<T.PsString[]>,
negative: boolean, negative: boolean,
kids: ListOfEntities,
): T.SingleOrLengthOpts<T.PsString[]>; ): T.SingleOrLengthOpts<T.PsString[]>;
function arrangePs( function compilePs(
subj: T.PsString[], nps: ListOfEntities,
obj: T.PsString[] | undefined,
head: T.PsString | undefined, head: T.PsString | undefined,
rest: T.SingleOrLengthOpts<T.PsString[]>, rest: T.SingleOrLengthOpts<T.PsString[]>,
negative: boolean, negative: boolean,
kids: ListOfEntities,
): T.SingleOrLengthOpts<T.PsString[]> { ): T.SingleOrLengthOpts<T.PsString[]> {
if ("long" in rest) { if ("long" in rest) {
return { return {
long: arrangePs(subj, obj, head, rest.long, negative), long: compilePs(nps, head, rest.long, negative, kids),
short: arrangePs(subj, obj, head, rest.short, negative), short: compilePs(nps, head, rest.short, negative, kids),
...rest.mini ? { ...rest.mini ? {
mini: arrangePs(subj, obj, head, rest.mini, negative), mini: compilePs(nps, head, rest.mini, negative, kids),
} : {}, } : {},
}; };
} }
const verbWNeg = arrangeVerbWNeg(head, rest, negative); const entities: ListOfEntities = [
if (obj) { ...nps,
return subj.flatMap(s => ( ...compileVerbWNegative(head, rest, negative)
obj.flatMap(o => ];
verbWNeg.flatMap(v => ( const entitiesWKids = putKidsInKidsSection(entities, kids);
concatPsString(s, " ", o, " ", v) return combineEntities(entitiesWKids);
// concatPsString(o, " ", s, " ", v), }
))
)) 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 => ( const toShrink = (VP.shrinkServant && VP.servant)
verbWNeg.flatMap(v => ( ? VP[VP.servant]
concatPsString(s, " ", v) : 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ú" }; const nu: T.PsString = { p: "نه", f: "nú" };
if (!head) { if (!head) {
return rest.map(r => concatPsString(nu, " ", removeAccents(r))); return [
[nu],
rest.map(r => removeAccents(r)),
];
} }
const regularPrefix = head.p === "و" || head.p === "وا"; // const regularPrefix = head.p === "و" || head.p === "وا";
const withNuAfterHead = rest.map(r => concatPsString( // if (regularPrefix) {
removeAccents(head), // dashes for oo-nu etc
{ p: "", f: "-" },
nu,
" ",
removeAccents(r),
));
if (regularPrefix) {
return withNuAfterHead;
}
const withNuBeforeHead = rest.map(r => concatPsString(
nu,
" ",
removeAccents(head),
removeAccents(r),
));
return [ return [
...withNuAfterHead, [removeAccents(head)],
...withNuBeforeHead, rest.map(r => concatPsString(nu, " ", removeAccents(r)))
]; ];
} }

View File

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