phrase builder working better

This commit is contained in:
lingdocs 2022-03-18 23:28:51 +04:00
parent 10f78df818
commit 1bf14eb44f
5 changed files with 101 additions and 56 deletions

View File

@ -3,28 +3,30 @@ import NPPicker from "../np-picker/NPPicker";
import VerbPicker from "../VerbPicker"; import VerbPicker from "../VerbPicker";
import VPDisplay from "./VPDisplay"; import VPDisplay from "./VPDisplay";
import ObjectDisplay from "./ObjectDisplay"; import ObjectDisplay from "./ObjectDisplay";
import { verbs } from "../../words/words"; import { verbs as verbsRaw } from "../../words/words";
import { import {
isInvalidSubjObjCombo, isInvalidSubjObjCombo,
} from "../../lib/np-tools"; } from "../../lib/np-tools";
function verbPhraseComplete({ subject, verb, shrinkServant }: { subject: NPSelection | undefined, verb: VerbSelection | undefined, shrinkServant: boolean }): VPSelection | undefined { const verbs = verbsRaw.filter(v => !v.entry.c?.includes("gramm."))
function verbPhraseComplete({ subject, verb }: { subject: NPSelection | undefined, verb: VerbSelection | undefined }): 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,
}; };
} }
// TODO: BIG ISSUE, IF YOU OPEN THE OBJECT PRONOUN BOX AND IT CONFLICTS WITH THE SUBJECT
// IT CAN SAY THE COMBO IS NOT ALLOWED AND SHOW SOMETHING BLANK
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
@ -57,40 +59,26 @@ export function PhraseBuilder() {
object, object,
}); });
} }
function handleShrinkServantChange(e: React.ChangeEvent<HTMLInputElement>) { const verbPhrase: VPSelection | undefined = verbPhraseComplete({ subject, verb });
setShrinkServant(e.target.checked); return <div className="mt-3">
} <div className="row">
const verbPhrase: VPSelection | undefined = verbPhraseComplete({ subject, verb, shrinkServant }); <div className="col mb-2">
return <div> <div className="h4">Subject</div>
<div className="d-flex flex-row justify-content-between">
<div className="mr-2">
<div className="h5">Subject</div>
<NPPicker <NPPicker
np={subject} np={subject}
counterPart={verb ? verb.object : undefined} counterPart={verb ? verb.object : undefined}
onChange={handleSubjectChange} onChange={handleSubjectChange}
/> />
</div> </div>
{verb && (verb.object !== "none") && <div className="mr-2"> {verb && (verb.object !== "none") && <div className="col mb-2">
<div className="h5">Object</div> <div className="h4">Object</div>
<ObjectDisplay object={verb.object} counterPart={subject} onChange={handleObjectChange} /> <ObjectDisplay object={verb.object} counterPart={subject} onChange={handleObjectChange} />
</div>} </div>}
<div> <div className="col mb-2">
<div className="h5">Verb</div> <div className="h4">Verb</div>
<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

@ -1,21 +1,68 @@
import { useState } from "react";
import { renderVP, compileVP } from "../../lib/phrase-building"; import { renderVP, compileVP } from "../../lib/phrase-building";
import { import {
InlinePs, InlinePs,
defaultTextOptions as opts, defaultTextOptions as opts,
ButtonSelect,
Types as T,
} from "@lingdocs/pashto-inflector"; } from "@lingdocs/pashto-inflector";
function adjustForm(form: FormVersion, servantShrinkable: boolean): FormVersion {
if (!servantShrinkable) {
return form === "shortest"
? "no king"
: form === "mini servant"
? "full"
: form;
}
return form;
}
function VPDisplay({ VP }: { VP: VPSelection }) { function VPDisplay({ VP }: { VP: VPSelection }) {
const [form, setForm] = useState<FormVersion>("full");
// TODO: Possibly put the servant shrinking in here after the render // TODO: Possibly put the servant shrinking in here after the render
const result = compileVP(renderVP(VP)); const result = compileVP(renderVP(VP), form);
const rPs = "long" in result.ps ? result.ps.long : result.ps; const servantShrinkable = VP.object && typeof VP.object === "object";
return <div className="text-center mt-2"> return <div className="text-center mt-2">
{rPs.map((r, i) => <div key={i}> <div className="my-3">
<InlinePs opts={opts}>{r}</InlinePs> <ButtonSelect
</div>)} small
options={[
{ value: "full", label: "Full" },
{ value: "no king", label: "No King" },
...servantShrinkable ? [{ value: "mini servant", label: "Mini Servant" }] : [],
...servantShrinkable ? [{ value: "shortest", label: "Shortest" }] : [],
]}
value={adjustForm(form, servantShrinkable)}
// @ts-ignore
handleChange={setForm}
/>
</div>
{"long" in result.ps ?
<div>
<div className="h6">Long Verb:</div>
<VariationLayer vs={result.ps.long} />
<div className="h6">Short Verb:</div>
<VariationLayer vs={result.ps.short} />
{result.ps.mini && <>
<div className="h6">Mini Verb:</div>
<VariationLayer vs={result.ps.mini} />
</>}
</div>
: <VariationLayer vs={result.ps} />
}
{result.e && <div className="text-muted"> {result.e && <div className="text-muted">
{result.e.map((e, i) => <div key={i}>{e}</div>)} {result.e.map((e, i) => <div key={i}>{e}</div>)}
</div>} </div>}
</div> </div>
} }
function VariationLayer({ vs }: { vs: T.PsString[] }) {
return <div className="mb-2">
{vs.map((r, i) => <div key={i}>
<InlinePs opts={opts}>{r}</InlinePs>
</div>)}
</div>;
}
export default VPDisplay; export default VPDisplay;

View File

@ -6,17 +6,19 @@ import {
getVerbBlockPosFromPerson, getVerbBlockPosFromPerson,
} from "@lingdocs/pashto-inflector"; } from "@lingdocs/pashto-inflector";
type ListOfEntities = T.PsString[][]; type ListOfEntities = (T.PsString & { isVerbPrefix?: boolean, prefixFollowedByParticle?: boolean })[][];
export function compileVP(VP: VPRendered): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] } { export function compileVP(VP: VPRendered, form: FormVersion): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] } {
const { head, rest } = VP.verb.ps; const { head, rest } = VP.verb.ps;
const { kids, NPs } = shrinkEntitiesAndGatherKids(VP); const { kids, NPs } = shrinkEntitiesAndGatherKids(VP, form);
return { return {
ps: compilePs(NPs, head, rest, VP.verb.negative, kids), ps: compilePs(NPs, head, rest, VP.verb.negative, kids),
e: compileEnglish(VP), e: compileEnglish(VP),
}; };
} }
// TODO: ISSUE off prefix-nu in the phonetics
function compilePs( function compilePs(
nps: ListOfEntities, nps: ListOfEntities,
head: T.PsString | undefined, head: T.PsString | undefined,
@ -49,33 +51,37 @@ function compilePs(
} }
const entities: ListOfEntities = [ const entities: ListOfEntities = [
...nps, ...nps,
...compileVerbWNegative(head, rest, negative) ...compileVerbWNegative(head, rest, negative),
]; ];
const entitiesWKids = putKidsInKidsSection(entities, kids); const entitiesWKids = putKidsInKidsSection(entities, kids);
return combineEntities(entitiesWKids); return combineEntities(entitiesWKids);
} }
function shrinkEntitiesAndGatherKids(VP: VPRendered): { kids: ListOfEntities, NPs: ListOfEntities } { function shrinkEntitiesAndGatherKids(VP: VPRendered, form: FormVersion): { kids: ListOfEntities, NPs: ListOfEntities } {
const main = { const main = {
subject: VP.subject.ps, subject: VP.subject.ps,
object: typeof VP.object === "object" ? VP.object.ps : undefined, object: typeof VP.object === "object" ? VP.object.ps : undefined,
} }
const toShrink = (VP.shrinkServant && VP.servant) const removeKing = form === "no king" || form === "shortest";
const shrinkServant = form === "mini servant" || form === "shortest";
const shrinkCanditate = ((form === "mini servant" || form === "shortest") && VP.servant)
? VP[VP.servant] ? VP[VP.servant]
: undefined; : undefined;
if (!toShrink || typeof toShrink !== "object") { const toShrink = (!shrinkCanditate || typeof shrinkCanditate !== "object")
return { ? undefined
kids: [], : shrinkCanditate;
NPs: [main.subject, ...main.object ? [main.object] : []] // TODO: big problem, the king removal doesn't work with grammatically transitive things
};
}
const king = main[VP.king]; const king = main[VP.king];
if (!king) { const showSubject = (VP.king === "subject" && !removeKing && king) || (VP.servant === "subject" && !shrinkServant);
throw new Error("lost the king in the shrinking process"); const showObject = (
} (VP.king === "object" && !removeKing && king) || (VP.servant === "object" && !shrinkServant)
);
return { return {
kids: [shrink(toShrink)], kids: [...toShrink ? [shrink(toShrink)] : []],
NPs: [king], NPs: [
...showSubject ? [main.subject] : [],
...(showObject && main.object) ? [main.object] : [],
],
} }
} }
@ -92,7 +98,11 @@ function putKidsInKidsSection(entities: ListOfEntities, kids: ListOfEntities): L
const first = entities[0]; const first = entities[0];
const rest = entities.slice(1); const rest = entities.slice(1);
return [ return [
first, first.map(x => (
x.isVerbPrefix &&
// TODO: This isn't quite working
(kids.length)
) ? { ...x, prefixFollowedByParticle: true } : x),
...kids, ...kids,
...rest, ...rest,
]; ];
@ -105,7 +115,9 @@ function combineEntities(loe: ListOfEntities): T.PsString[] {
return combineEntities(rest).flatMap(r => ( return combineEntities(rest).flatMap(r => (
first.map(ps => concatPsString( first.map(ps => concatPsString(
ps, ps,
ps.p === "و" ? { p: "", f: "-" } : " ", (ps.prefixFollowedByParticle
? { p: "", f: "-" }
: ps.isVerbPrefix ? "" : " "),
r, r,
)) ))
)); ));
@ -115,7 +127,7 @@ function combineEntities(loe: ListOfEntities): T.PsString[] {
function compileVerbWNegative(head: T.PsString | undefined, rest: T.PsString[], negative: boolean): ListOfEntities { function compileVerbWNegative(head: T.PsString | undefined, rest: T.PsString[], negative: boolean): ListOfEntities {
if (!negative) { if (!negative) {
return [ return [
...head ? [[head]] : [], ...head ? [[{...head, isVerbPrefix: true}]] : [],
rest, rest,
]; ];
} }
@ -130,7 +142,7 @@ function compileVerbWNegative(head: T.PsString | undefined, rest: T.PsString[],
// if (regularPrefix) { // if (regularPrefix) {
// dashes for oo-nu etc // dashes for oo-nu etc
return [ return [
[removeAccents(head)], [{ ...removeAccents(head), isVerbPrefix: true }],
rest.map(r => concatPsString(nu, " ", removeAccents(r))) rest.map(r => concatPsString(nu, " ", removeAccents(r)))
]; ];
} }

View File

@ -39,7 +39,6 @@ export function renderVP(VP: VPSelection): VPRendered {
servant, servant,
isPast, isPast,
isTransitive, 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),

View File

@ -10,7 +10,6 @@ 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<>
@ -18,7 +17,6 @@ type VPRendered = {
type: "VPRendered", type: "VPRendered",
king: "subject" | "object", king: "subject" | "object",
servant: "subject" | "object" | undefined, servant: "subject" | "object" | undefined,
shrinkServant: boolean,
isPast: boolean, isPast: boolean,
isTransitive: boolean, isTransitive: boolean,
subject: Rendered<NPSelection>, subject: Rendered<NPSelection>,
@ -96,6 +94,7 @@ type ParticipleSelection = {
// If T has key K ("user"), replace it // If T has key K ("user"), replace it
type ReplaceKey<T, K extends string, R> = T extends Record<K, unknown> ? (Omit<T, K> & Record<K, R>) : T; type ReplaceKey<T, K extends string, R> = T extends Record<K, unknown> ? (Omit<T, K> & Record<K, R>) : T;
type FormVersion = "full" | "no king" | "mini servant" | "shortest"; // TODO: "all";
type Rendered<T extends NPSelection> = ReplaceKey< type Rendered<T extends NPSelection> = ReplaceKey<
Omit<T, "changeGender" | "changeNumber" | "changeDistance">, Omit<T, "changeGender" | "changeNumber" | "changeDistance">,