UGLY basic ability to shrink servants
This commit is contained in:
parent
06fa7966f8
commit
5c510a6128
|
@ -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>}
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
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),
|
||||
));
|
||||
return [
|
||||
...withNuAfterHead,
|
||||
...withNuBeforeHead,
|
||||
[nu],
|
||||
rest.map(r => removeAccents(r)),
|
||||
];
|
||||
}
|
||||
// const regularPrefix = head.p === "و" || head.p === "وا";
|
||||
// if (regularPrefix) {
|
||||
// dashes for oo-nu etc
|
||||
return [
|
||||
[removeAccents(head)],
|
||||
rest.map(r => concatPsString(nu, " ", removeAccents(r)))
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue