now working with dynamic compounds and OSV

This commit is contained in:
lingdocs 2022-03-28 11:08:08 +05:00
parent 4f79456115
commit 693aecdd70
13 changed files with 156 additions and 82 deletions

View File

@ -1,11 +1,13 @@
import Select from "react-select";
import {
makeNounSelection,
makeVerbSelectOption,
zIndexProps,
} from "./np-picker/picker-tools";
import {
Types as T,
ButtonSelect,
getVerbInfo,
} from "@lingdocs/pashto-inflector";
const tenseOptions: { label: string, value: VerbTense }[] = [{
@ -29,42 +31,54 @@ const tenseOptions: { label: string, value: VerbTense }[] = [{
}];
function makeVerbSelection(verb: VerbEntry, oldVerbSelection?: VerbSelection): VerbSelection {
const info = getVerbInfo(verb.entry, verb.complement);
function getTransObjFromOldVerbSelection() {
if (!oldVerbSelection || oldVerbSelection.object === "none" || typeof oldVerbSelection.object === "number") return undefined;
if (
!oldVerbSelection ||
oldVerbSelection.object === "none" ||
typeof oldVerbSelection.object === "number" ||
oldVerbSelection.isCompound === "dynamic" ||
(oldVerbSelection.object?.type === "noun" && oldVerbSelection.object.dynamicComplement)
) return undefined;
return oldVerbSelection.object;
}
// TODO: more complex types and unchangeable dynamic compound objects
// TODO: use proper type predicates
const transitivity: "intransitive" | "transitive" | "grammaticallyTransitive" = verb.entry.c?.includes("intrans.")
? "intransitive"
: verb.entry.c?.includes("v. gramm. trans.")
? "grammaticallyTransitive"
: "transitive";
const object = (transitivity === "grammaticallyTransitive")
const transitivity: T.Transitivity = "grammaticallyTransitive" in info
? "grammatically transitive"
: info.transitivity;
const object = (transitivity === "grammatically transitive")
? T.Person.ThirdPlurMale
: transitivity === "transitive"
: info.type === "dynamic compound"
? makeNounSelection(info.objComplement.entry as NounEntry, true)
: (transitivity === "transitive")
? getTransObjFromOldVerbSelection()
: "none";
// TODO: better here based on selection of which type
const isCompound = verb.entry.c?.includes("stat. comp.")
const isCompound = "stative" in info
? "stative"
: verb.entry.c?.includes("dyn. comp.")
: info.type === "dynamic compound"
? "dynamic"
: false;
const dynAuxVerb: VerbEntry | undefined = isCompound !== "dynamic"
? undefined
: info.type === "dynamic compound"
? { entry: info.auxVerb } as VerbEntry
: "dynamic" in info
? { entry: info.dynamic.auxVerb } as VerbEntry
: undefined;
return {
type: "verb",
verb,
verb: verb,
dynAuxVerb,
tense: oldVerbSelection ? oldVerbSelection.tense : "present",
object,
transitivity,
isCompound,
negative: oldVerbSelection ? oldVerbSelection.negative : false,
...verb.entry.c?.includes("v. trans./gramm. trans") ? {
...("grammaticallyTransitive" in info) ? {
changeTransitivity: function (t) {
return {
...this,
transitivity: t,
object: t === "grammaticallyTransitive" ? T.Person.ThirdPlurMale : undefined,
object: t === "grammatically transitive" ? T.Person.ThirdPlurMale : undefined,
};
},
} : {},
@ -97,10 +111,10 @@ function VerbPicker({ onChange, verb, verbs }: { verbs: VerbEntry[], verb: VerbS
});
}
}
function notInstransitive(t: "transitive" | "intransitive" | "grammaticallyTransitive"): "transitive" | "grammaticallyTransitive" {
function notInstransitive(t: "transitive" | "intransitive" | "grammatically transitive"): "transitive" | "grammatically transitive" {
return t === "intransitive" ? "transitive" : t;
}
function handleChangeTransitivity(t: "transitive" | "grammaticallyTransitive") {
function handleChangeTransitivity(t: "transitive" | "grammatically transitive") {
if (verb && verb.changeTransitivity) {
onChange(verb.changeTransitivity(t));
}
@ -150,7 +164,7 @@ function VerbPicker({ onChange, verb, verbs }: { verbs: VerbEntry[], verb: VerbS
small
options={[{
label: "gramm. trans.",
value: "grammaticallyTransitive",
value: "grammatically transitive",
}, {
label: "trans.",
value: "transitive",

View File

@ -6,9 +6,10 @@ import {
} from "./picker-tools";
import {
ButtonSelect,
InlinePs,
defaultTextOptions as opts,
} from "@lingdocs/pashto-inflector";
function NPNounPicker({ onChange, noun, nouns }: { nouns: NounEntry[], noun: NounSelection | undefined, onChange: (p: NounSelection) => void }) {
const options = nouns.sort((a, b) => (a.p.localeCompare(b.p, "af-PS"))).map(makeSelectOption)
function onEntrySelect({ value }: { label: string, value: string }) {
@ -20,7 +21,7 @@ function NPNounPicker({ onChange, noun, nouns }: { nouns: NounEntry[], noun: Nou
onChange(makeNounSelection(entry));
}
return <div style={{ maxWidth: "225px", minWidth: "125px" }}>
<Select
{!(noun && noun.dynamicComplement) ? <Select
value={noun && noun.entry.ts.toString()}
// @ts-ignore
onChange={onEntrySelect}
@ -32,7 +33,17 @@ function NPNounPicker({ onChange, noun, nouns }: { nouns: NounEntry[], noun: Nou
placeholder={noun ? options.find(o => o.value === (noun.entry).ts.toString())?.label : "Select Noun..."}
{...zIndexProps}
/>
/> : <div>
{noun && <div>
<div className="mb-2">Included in Dyn. Compound:</div>
<div className="mb-3 text-center">
<InlinePs opts={opts}>
{{ p: noun.entry.p, f: noun.entry.f }}
</InlinePs>
<div className="text-muted">{noun.entry.e}</div>
</div>
</div>}
</div>}
{noun && <div className="my-2 d-flex flex-row justify-content-around align-items-center">
<div>
{noun.changeGender ? <ButtonSelect

View File

@ -4,16 +4,24 @@ import ParticiplePicker from "./NPParticiplePicker";
// import { getEnglishPronoun } from "../../lib/english-pronoun-tools";
// import { ButtonSelect } from "@lingdocs/pashto-inflector";
import { randomPerson } from "../../lib/np-tools";
import { useState } from "react";
import { useState, useEffect } from "react";
import { nouns, verbs } from "../../words/words";
// import { capitalizeFirstLetter } from "../../lib/text-tools";
const npTypes: NPType[] = ["pronoun", "noun", "participle"];
function NPPicker({ np, onChange, counterPart, asObject }: { onChange: (nps: NPSelection | undefined) => void, np: NPSelection | undefined, counterPart: NPSelection | VerbObject | undefined, asObject?: boolean }) {
// eslint-disable-next-line
function NPPicker({ np, onChange, counterPart, asObject }: {
onChange: (nps: NPSelection | undefined) => void,
np: NPSelection | undefined,
counterPart: NPSelection | VerbObject | undefined,
asObject?: boolean,
}) {
const [npType, setNpType] = useState<NPType | undefined>(np ? np.type : undefined);
useEffect(() => {
setNpType(np ? np.type : undefined);
}, [np]);
function handleClear() {
if (np && np.type === "noun" && np.dynamicComplement) return;
setNpType(undefined);
onChange(undefined);
}
@ -32,8 +40,9 @@ function NPPicker({ np, onChange, counterPart, asObject }: { onChange: (nps: NPS
setNpType(ntp);
}
}
const isDynamicComplement = np && np.type === "noun" && np.dynamicComplement;
return <div>
{!npType ? <div className="text-center mt-3">
{!npType && <div className="text-center mt-3">
{npTypes.map((npt) => <div className="mb-2">
<button
key={npt}
@ -44,19 +53,16 @@ function NPPicker({ np, onChange, counterPart, asObject }: { onChange: (nps: NPS
{npt}
</button>
</div>)}
</div>
: <button className="btn btn-sm btn-light mb-2" onClick={handleClear}>X</button>}
{np ?
((np.type === "pronoun"
? <PronounPicker asObject={asObject} pronoun={np} onChange={onChange} />
: np.type === "noun"
? <NounPicker nouns={nouns} noun={np} onChange={onChange} />
: <ParticiplePicker verbs={verbs} participle={np} onChange={onChange} />))
: (npType === "noun")
? <NounPicker nouns={nouns} noun={np} onChange={onChange} />
: (npType === "participle")
? <ParticiplePicker verbs={verbs} participle={np} onChange={onChange} />
: null}
</div>}
{(npType && !isDynamicComplement) && <button className="btn btn-sm btn-light mb-2" onClick={handleClear}>X</button>}
{(npType === "pronoun" && np?.type === "pronoun")
? <PronounPicker asObject={asObject} pronoun={np} onChange={onChange} />
: npType === "noun"
? <NounPicker nouns={nouns} noun={(np && np.type === "noun") ? np : undefined} onChange={onChange} />
: npType === "participle"
? <ParticiplePicker verbs={verbs} participle={(np && np.type === "participle") ? np : undefined} onChange={onChange} />
: null
}
</div>;
}

View File

@ -45,13 +45,14 @@ export function makeSelectOption(e: VerbEntry | NounEntry | AdjectiveEntry | Loc
};
}
export function makeNounSelection(entry: NounEntry): NounSelection {
export function makeNounSelection(entry: NounEntry, dynamicComplement?: true): NounSelection {
const number = isPluralNounEntry(entry) ? "plural" : "singular";
return {
type: "noun",
entry,
gender: isMascNounEntry(entry) ? "masc" : "fem",
number,
dynamicComplement,
...isUnisexNounEntry(entry) ? {
changeGender: function(gender: T.Gender): NounSelection {
return {

View File

@ -85,7 +85,7 @@ function AbbreviationFormSelector({ form, onChange, adjustable }: {
}
}
// TODO: limit display of shrinking options based on the verb type
return <div className="my-3">
return <div className="mb-3">
{/* <div className="text-center text-small mb-2">Abbreviation Options</div> */}
<ButtonSelect
// @ts-ignore

View File

@ -3,12 +3,17 @@ import NPPicker from "../np-picker/NPPicker";
function ObjectDisplay({ object, onChange, counterPart }: {
object: Exclude<VerbObject, "none">,
onChange: (o: NPSelection | undefined) => void,
counterPart: NPSelection | undefined },
) {
counterPart: NPSelection | undefined ,
}) {
return <div>
{(typeof object === "number")
? <div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div>
: <NPPicker asObject np={object} counterPart={counterPart} onChange={onChange} />}
: <NPPicker
asObject
np={object}
counterPart={counterPart}
onChange={onChange}
/>}
</div>;
}

View File

@ -27,7 +27,6 @@ export function PhraseBuilder() {
function handleObjectChange(object: NPSelection | undefined) {
if (!verb) return;
if ((verb.object === "none") || (typeof verb.object === "number")) return;
console.log("handling object change and checking", object);
// check for pronoun conflict
if (hasPronounConflict(subject, object)) {
alert("That combination of pronouns is not allowed");
@ -36,6 +35,7 @@ export function PhraseBuilder() {
setVerb({ ...verb, object });
}
function handleSubjObjSwap() {
if (verb?.isCompound === "dynamic") return;
const output = switchSubjObj({ subject, verb });
setSubject(output.subject);
setVerb(output.verb);
@ -47,7 +47,8 @@ export function PhraseBuilder() {
<div>{kingEmoji} = <abbr title="controls the verb conjugation, can be removed">king</abbr> of phrase</div>
<div>{servantEmoji} = <abbr title="can be shrunken into a mini-pronoun">servant</abbr> of phrase</div>
</div>
{verb && (typeof verb.object === "object") && <div className="d-flex flex-row justify-content-around flex-wrap mb-2">
{(verb && (typeof verb.object === "object") && (verb.isCompound !== "dynamic")) &&
<div className="d-flex flex-row justify-content-around flex-wrap mb-2">
<button onClick={handleSubjObjSwap} className="btn btn-sm btn-light">
<i className="fas fa-exchange-alt mr-2" /> subj/obj
</button>
@ -64,7 +65,11 @@ export function PhraseBuilder() {
</div>
{verb && (verb.object !== "none") && <div className="mb-2">
<div className="h4">Object {showRole(VPRendered, "object")}</div>
<ObjectDisplay object={verb.object} counterPart={subject} onChange={handleObjectChange} />
<ObjectDisplay
object={verb.object}
counterPart={subject}
onChange={handleObjectChange}
/>
</div>}
<div className="mb-2">
<div className="h4">Verb</div>

View File

@ -8,10 +8,25 @@ import {
import AbbreviationFormSelector from "./AbbreviationFormSelector";
import { isPastTense } from "../../lib/phrase-building/vp-tools";
// TODO: Issue when picking dynamic compound and then going back with the object dissappearing
function VPDisplay({ VP }: { VP: VPSelection }) {
const [form, setForm] = useState<FormVersion>({ removeKing: false, shrinkServant: false });
const result = compileVP(renderVP(VP), form);
const [OSV, setOSV] = useState<boolean>(false);
const result = compileVP(renderVP(VP), { ...form, OSV });
return <div className="text-center mt-2">
<div className="form-check mb-2">
<input
className="form-check-input"
type="checkbox"
checked={OSV}
id="OSVCheckbox"
onChange={e => setOSV(e.target.checked)}
/>
<label className="form-check-label text-muted" htmlFor="OSVCheckbox">
Include O S V
</label>
</div>
<AbbreviationFormSelector
adjustable={whatsAdjustable(VP)}
form={form}
@ -37,7 +52,10 @@ function VPDisplay({ VP }: { VP: VPSelection }) {
}
function whatsAdjustable(VP: VPSelection): "both" | "king" | "servant" {
return VP.verb.transitivity === "transitive"
// TODO: intransitive dynamic compounds?
return (VP.verb.isCompound === "dynamic" && VP.verb.transitivity === "transitive")
? (isPastTense(VP.verb.tense) ? "servant" : "king")
: VP.verb.transitivity === "transitive"
? "both"
: VP.verb.transitivity === "intransitive"
? "king"

View File

@ -9,9 +9,10 @@ import { removeBa } from "./vp-tools";
// TODO: make it an option to include O S V order ?
// TODO: tu ba laaR nu she hyphens all messed up
export function compileVP(VP: VPRendered, form: FormVersion): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] };
export function compileVP(VP: VPRendered, form: FormVersion, combineLengths: true): { ps: T.PsString[], e?: string [] };
export function compileVP(VP: VPRendered, form: FormVersion, combineLengths?: true): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] } {
type Form = FormVersion & { OSV?: boolean };
export function compileVP(VP: VPRendered, form: Form): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] };
export function compileVP(VP: VPRendered, form: Form, combineLengths: true): { ps: T.PsString[], e?: string [] };
export function compileVP(VP: VPRendered, form: Form, combineLengths?: true): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] } {
const verb = VP.verb.ps;
const { kids, NPs } = getSegmentsAndKids(VP, form);
const psResult = compilePs({
@ -65,24 +66,33 @@ function compilePs({ NPs, kids, verb: { head, rest }, isCompound, negative }: Co
));
}
function getSegmentsAndKids(VP: VPRendered, form: FormVersion): { kids: Segment[], NPs: Segment[][] } {
const main = {
function getSegmentsAndKids(VP: VPRendered, form: Form): { kids: Segment[], NPs: Segment[][] } {
const SO = {
subject: VP.subject.ps,
object: typeof VP.object === "object" ? VP.object.ps : undefined,
}
// TODO: simplify all this logic
const { removeKing, shrinkServant } = form;
const shrinkCanditate = (shrinkServant && VP.servant)
? VP[VP.servant]
: undefined;
const toShrink = (!shrinkCanditate || typeof shrinkCanditate !== "object")
? undefined
: shrinkCanditate;
const king = main[VP.king];
const showSubject = (VP.king === "subject" && !removeKing && king) || (VP.servant === "subject" && !shrinkServant);
const showObject = (
(king && VP.king === "object" && !removeKing) || (VP.servant === "object" && !shrinkServant)
);
const removeKing = form.removeKing && !(VP.isCompound === "dynamic" && VP.isPast);
const shrinkServant = form.shrinkServant && !(VP.isCompound === "dynamic" && !VP.isPast);
const toShrink = (() => {
if (!shrinkServant) return undefined;
if (!VP.servant) return undefined;
const servant = VP[VP.servant];
if (typeof servant !== "object") return undefined;
return servant;
})();
function getSegment(t: "subject" | "object"): Segment | undefined {
const word = (VP.servant === t)
? (!shrinkServant ? SO[t] : undefined)
: (VP.king === t)
? (!removeKing ? SO[t] : undefined)
: undefined;
if (!word) return undefined;
return makeSegment(word);
}
const subject = getSegment("subject");
const object = getSegment("object");
return {
kids: [
...VP.verb.hasBa
@ -92,17 +102,14 @@ function getSegmentsAndKids(VP: VPRendered, form: FormVersion): { kids: Segment[
],
NPs: [
[
...showSubject ? [makeSegment(main.subject)] : [],
...(showObject && main.object) ? [makeSegment(main.object)] : [],
...subject ? [subject] : [],
...object ? [object] : [],
],
// TODO: make this an option to also include O S V order ??
// also show O S V if both are showing
...(main.object && showObject && showSubject) ? [[
makeSegment(main.object),
makeSegment(main.subject),
]] : [],
...(subject && object && form.OSV) ? [[object, subject]] : [],
],
}
};
}
function putKidsInKidsSection(segments: Segment[], kids: Segment[]): Segment[] {

View File

@ -52,15 +52,15 @@ export function renderVP(VP: VPSelection): VPRendered {
verb: renderVerbSelection(VP.verb, kingPerson, objectPerson),
englishBase: renderEnglishVPBase({
subjectPerson,
object: VP.object,
object: VP.verb.isCompound === "dynamic" ? "none" : VP.object,
vs: VP.verb,
}),
};
}
function renderNPSelection(NP: NPSelection, inflected: boolean, inflectEnglish: boolean, role: "subject"): Rendered<NPSelection>
function renderNPSelection(NP: NPSelection | ObjectNP, inflected: boolean, inflectEnglish: boolean, role: "object"): Rendered<NPSelection> | T.Person.ThirdPlurMale | "none";
function renderNPSelection(NP: NPSelection | ObjectNP, inflected: boolean, inflectEnglish: boolean, role: "subject" | "object"): Rendered<NPSelection> | T.Person.ThirdPlurMale | "none" {
export function renderNPSelection(NP: NPSelection, inflected: boolean, inflectEnglish: boolean, role: "subject"): Rendered<NPSelection>
export function renderNPSelection(NP: NPSelection | ObjectNP, inflected: boolean, inflectEnglish: boolean, role: "object"): Rendered<NPSelection> | T.Person.ThirdPlurMale | "none";
export function renderNPSelection(NP: NPSelection | ObjectNP, inflected: boolean, inflectEnglish: boolean, role: "subject" | "object"): Rendered<NPSelection> | T.Person.ThirdPlurMale | "none" {
if (typeof NP !== "object") {
if (role !== "object") {
throw new Error("ObjectNP only allowed for objects");
@ -125,7 +125,8 @@ function renderParticipleSelection(p: ParticipleSelection, inflected: boolean):
}
function renderVerbSelection(vs: VerbSelection, person: T.Person, objectPerson: T.Person | undefined): VerbRendered {
const conjugations = conjugateVerb(vs.verb.entry, vs.verb.complement);
const v = vs.dynAuxVerb || vs.verb;
const conjugations = conjugateVerb(v.entry, v.complement);
// TODO: error handle this?
const conj = "grammaticallyTransitive" in conjugations
// if there's an option for grammatically transitive or transitive

View File

@ -31,11 +31,12 @@ type VerbTense = "present" | "subjunctive" | "perfectiveFuture" | "imperfectiveF
type VerbSelection = {
type: "verb",
verb: VerbEntry,
dynAuxVerb?: VerbEntry,
tense: VerbTense,
object: VerbObject, // TODO: should have a locked in (but number changeable noun) here for dynamic compounds
transitivity: "transitive" | "intransitive" | "grammaticallyTransitive",
transitivity: import("@lingdocs/pashto-inflector").Types.Transitivity,
isCompound: "stative" | "dynamic" | false,
changeTransitivity?: (t: "transitive" | "grammaticallyTransitive") => VerbSelection,
changeTransitivity?: (t: "transitive" | "grammatically transitive") => VerbSelection,
// TODO: changeStativeDynamic
// TODO: add in aspect element here??
negative: boolean,
@ -73,6 +74,7 @@ type NounSelection = {
entry: NounEntry,
gender: import("@lingdocs/pashto-inflector").Types.Gender,
number: NounNumber,
dynamicComplement?: boolean,
// TODO: Implement
// adjectives: [],
// TODO: Implement

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,4 @@
module.exports = [
{ ts: 1527812732, e: "to work" }, // کار کول
{ ts: 1527812939, e: "to run" }, // منډې وهل
];