sandwiches in eqcomplement and adjectives

This commit is contained in:
lingdocs 2022-05-21 17:00:16 -05:00
parent e8ec806ecb
commit 5c9b7b7c3e
13 changed files with 156 additions and 48 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@lingdocs/pashto-inflector",
"version": "2.5.6",
"version": "2.5.7",
"author": "lingdocs.com",
"description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations",
"homepage": "https://verbs.lingdocs.com",

View File

@ -146,6 +146,7 @@ export function SandwichSelect<E extends T.Sandwich>(props: {
props.onChange(s);
}
return <div style={divStyle}>
<div>Sandwich Base</div>
<Select
styles={customStyles}
isSearchable={true}

View File

@ -92,6 +92,7 @@ function EPExplorer(props: {
onChange={payload => adjustEps({ type: "set predicate NP", payload })}
opts={props.opts}
/> : <EqCompPicker
phraseIsComplete={phraseIsComplete}
comp={eps.predicate.type === "Complement" ? eps.predicate.Complement : undefined}
onChange={payload => adjustEps({ type: "set predicate comp", payload })}
opts={props.opts}

View File

@ -6,6 +6,7 @@ import SandwichPicker from "../../np-picker/SandwichPicker";
const compTypes: T.EqCompType[] = ["adjective", "loc. adv.", "sandwich"];
function EqCompPicker(props: {
phraseIsComplete: boolean,
onChange: (comp: T.EqCompSelection | undefined) => void,
comp: T.EqCompSelection | undefined,
opts: T.TextOptions,
@ -62,10 +63,11 @@ function EqCompPicker(props: {
<div style={{ minWidth: "9rem" }}>
{compType === "adjective" ?
<AdjectivePicker
entryFeeder={props.entryFeeder.adjectives}
entryFeeder={props.entryFeeder}
adjective={props.comp?.type === "adjective" ? props.comp : undefined}
opts={props.opts}
onChange={props.onChange}
phraseIsComplete={props.phraseIsComplete}
/>
: compType === "loc. adv."
? <LocativeAdverbPicker
@ -81,7 +83,7 @@ function EqCompPicker(props: {
sandwich={props.comp?.type === "sandwich" ? props.comp : undefined}
entryFeeder={props.entryFeeder}
// TODO: get phraseIsComplete working here
phraseIsComplete={false}
phraseIsComplete={props.phraseIsComplete}
/>
: null}
</div>

View File

@ -4,9 +4,10 @@ import AdjectivePicker from "./AdjectivePicker";
function AdjectiveManager(props: {
adjectives: T.AdjectiveSelection[],
entryFeeder: T.EntryFeederSingleType<T.AdjectiveEntry>,
entryFeeder: T.EntryFeeder,
opts: T.TextOptions,
onChange: (adjs: T.AdjectiveSelection[]) => void,
phraseIsComplete: boolean,
}) {
const [adding, setAdding] = useState<boolean>(false);
function handleChange(i: number) {
@ -44,6 +45,7 @@ function AdjectiveManager(props: {
</div>
</div>
<AdjectivePicker
phraseIsComplete={props.phraseIsComplete}
noTitle
adjective={undefined}
entryFeeder={props.entryFeeder}
@ -64,6 +66,7 @@ function AdjectiveManager(props: {
</div>
</div>
<AdjectivePicker
phraseIsComplete={props.phraseIsComplete}
noTitle
key={`adj${i}`}
adjective={adj}

View File

@ -1,25 +1,54 @@
import { useState } from "react";
import * as T from "../../types";
import EntrySelect from "../EntrySelect";
import SandwichPicker from "./SandwichPicker";
function AdjectivePicker(props: {
entryFeeder: T.EntryFeederSingleType<T.AdjectiveEntry>,
entryFeeder: T.EntryFeeder,
adjective: T.AdjectiveSelection | undefined,
onChange: (p: T.AdjectiveSelection | undefined) => void,
opts: T.TextOptions,
noTitle?: boolean,
phraseIsComplete: boolean,
}) {
const [addingSandwich, setAddingSandwich] = useState<boolean>(false);
function onEntrySelect(entry: T.AdjectiveEntry | undefined) {
if (!entry) {
return props.onChange(undefined);
}
props.onChange(makeAdjectiveSelection(entry));
}
function handleSandwichChange(s: T.SandwichSelection<T.Sandwich> | undefined) {
if (!props.adjective) return;
props.onChange({
...props.adjective,
sandwich: s,
});
if (!s) {
setAddingSandwich(false);
}
}
return <div style={{ maxWidth: "225px", minWidth: "125px" }}>
{!props.noTitle && <h6>Adjective</h6>}
<div>
{(props.adjective?.sandwich || addingSandwich) && <SandwichPicker
onChange={handleSandwichChange}
opts={props.opts}
sandwich={props.adjective?.sandwich}
entryFeeder={props.entryFeeder}
// TODO: get phraseIsComplete working here
phraseIsComplete={props.phraseIsComplete}
/>}
<div className="d-flex flex-row justify-content-between align-items-baseline">
{!props.noTitle && <div>
<h6>Adjective</h6>
</div>}
{(!addingSandwich && props.adjective && !props.adjective?.sandwich)
? <div className="clickable" onClick={() => setAddingSandwich(true)}>+ Sandwich</div>
: <div></div>}
</div>
<div className="mt-1">
<EntrySelect
value={props.adjective?.entry}
entryFeeder={props.entryFeeder}
entryFeeder={props.entryFeeder.adjectives}
onChange={onEntrySelect}
name="Adjective"
opts={props.opts}
@ -32,6 +61,7 @@ function makeAdjectiveSelection(entry: T.AdjectiveEntry): T.AdjectiveSelection {
return {
type: "adjective",
entry: entry,
sandwich: undefined,
};
}

View File

@ -60,6 +60,7 @@ function NPNounPicker(props: {
noun: T.NounSelection | undefined,
onChange: (p: T.NounSelection | undefined) => void,
opts: T.TextOptions,
phraseIsComplete: boolean,
}) {
// const [patternFilter, setPatternFilter] = useState<FilterPattern | undefined>(undefined);
// const [showFilter, setShowFilter] = useState<boolean>(false)
@ -99,8 +100,9 @@ function NPNounPicker(props: {
/>
</div>} */}
{props.noun && <AdjectiveManager
phraseIsComplete={props.phraseIsComplete}
adjectives={props.noun?.adjectives}
entryFeeder={props.entryFeeder.adjectives}
entryFeeder={props.entryFeeder}
opts={props.opts}
onChange={handelAdjectivesUpdate}
/>}

View File

@ -52,7 +52,9 @@ function NPPicker(props: {
setNpType(ntp);
onChange(pronoun);
} else {
onChange(undefined);
if (props.np) {
onChange(undefined);
}
setNpType(ntp);
}
}
@ -126,7 +128,7 @@ function NPPicker(props: {
}}>
<div className="d-flex flex-row text-muted mb-2">
<div>{possesiveLabel}:</div>
{(props.np.possesor && !props.isShrunk) && <div className="clickable ml-3 mr-2" onClick={handleToggleShrunken}>
{(props.np.possesor && !props.isShrunk && props.phraseIsComplete) && <div className="clickable ml-3 mr-2" onClick={handleToggleShrunken}>
{!props.np.possesor.shrunken ? "🪄" : "👶"}
</div>}
<div className="clickable ml-2" onClick={() => {
@ -160,6 +162,7 @@ function NPPicker(props: {
/>
: npType === "noun"
? <NounPicker
phraseIsComplete={props.phraseIsComplete}
entryFeeder={props.entryFeeder}
noun={(props.np && props.np.type === "noun") ? props.np : undefined}
onChange={onChange}

View File

@ -37,6 +37,14 @@ function SandwichPicker(props: {
});
}
return <div>
<div className="d-flex flex-row justify-content-between">
<div></div>
<div className="text-center">🥪 Sandwich</div>
<div className="clickable" onClick={() => props.onChange(undefined)}>
<i className="fas fa-trash" />
</div>
</div>
<div style={{ border: "1px #6C757D solid", padding: "3px" }}>
{sandwichBase && <div className="mb-2" style={{ margin: "0 auto" }}>
<NPPicker
onChange={handleNounChange}
@ -46,7 +54,9 @@ function SandwichPicker(props: {
role="object"
cantClear={true}
entryFeeder={props.entryFeeder}
phraseIsComplete={props.phraseIsComplete}
// TODO: the shrinking of possesives in sandwiches gets messed up with compilinig 😩
// disabling it for now
phraseIsComplete={false}
/>
</div>}
<SandwichSelect
@ -56,6 +66,7 @@ function SandwichPicker(props: {
value={sandwichBase}
onChange={handleSandwichChange}
/>
</div>
</div>;
}

View File

@ -456,50 +456,71 @@ export function findPossesivesToShrinkInVP(VP: T.VPRendered, f: {
function findPossesives(...nps: (T.Rendered<T.NPSelection> | T.ObjectNP | undefined)[]): T.Rendered<T.NPSelection>[] {
return nps.reduce((accum, curr) => {
const res = findPossesiveInNP(curr);
if (res) return [...accum, res];
const res = findPossesivesInNP(curr);
if (res) return [...accum, ...res];
return accum;
}, [] as T.Rendered<T.NPSelection>[]);
}
function findPossesiveInNP(NP: T.Rendered<T.NPSelection> | T.ObjectNP | undefined): T.Rendered<T.NPSelection> | undefined {
if (NP === undefined) return undefined;
if (typeof NP !== "object") return undefined;
if (!NP.possesor) return undefined;
if (NP.possesor.shrunken) {
return NP.possesor.np;
function findPossesivesInNP(NP: T.Rendered<T.NPSelection> | T.ObjectNP | undefined): T.Rendered<T.NPSelection>[] {
if (NP === undefined) return [];
if (typeof NP !== "object") return [];
if (!NP.possesor) return [];
if (NP.adjectives) {
const { adjectives, ...rest } = NP;
return [
...findPossesivesInAdjectives(adjectives),
...findPossesivesInNP(rest),
];
}
return findPossesiveInNP(NP.possesor.np);
if (NP.possesor.shrunken) {
return [NP.possesor.np];
}
return findPossesivesInNP(NP.possesor.np);
}
function findPossesivesInAdjectives(a: T.Rendered<T.AdjectiveSelection>[]): T.Rendered<T.NPSelection>[] {
return a.reduce((accum, curr): T.Rendered<T.NPSelection>[] => ([
...accum,
...findPossesivesInAdjective(curr),
]), [] as T.Rendered<T.NPSelection>[])
}
function findPossesivesInAdjective(a: T.Rendered<T.AdjectiveSelection>): T.Rendered<T.NPSelection>[] {
if (!a.sandwich) return [];
return findPossesivesInNP(a.sandwich.inside);
}
type FoundNP = {
np: T.Rendered<T.NPSelection>,
from: "subject" | "predicate",
from: "subject" | "predicate" | "AP",
};
export function findPossesivesToShrinkInEP(EP: T.EPRendered): FoundNP[] {
const inSubject = findPossesiveInNP(EP.subject);
const inPredicate = (EP.predicate.type === "adjective" || EP.predicate.type === "loc. adv.")
? undefined
: findPossesiveInNP(
const inSubject: FoundNP[] = findPossesivesInNP(EP.subject).map(np => ({ np, from: "subject" }));
const inPredicate: FoundNP[] = ((EP.predicate.type === "loc. adv.")
? []
: (EP.predicate.type === "adjective")
? findPossesivesInAdjective(EP.predicate)
: findPossesivesInNP(
// @ts-ignore - ts being dumb
EP.predicate as T.NPSelection
);
)).map(np => ({ np, from: "predicate" }));
return [
...inSubject ? [{ np: inSubject, from: "subject"} as FoundNP] : [],
...inPredicate ? [{ np: inPredicate, from: "predicate" } as FoundNP] : [],
...inSubject,
...inPredicate,
].filter(found => !(found.from === "subject" && EP.omitSubject));
}
export function findPossesiveToShrinkInVP(VP: T.VPRendered): T.Rendered<T.NPSelection> | undefined {
const obj: T.Rendered<T.NPSelection> | undefined = ("object" in VP && typeof VP.object === "object")
? VP.object
: undefined;
return (
findPossesiveInNP(VP.subject)
||
findPossesiveInNP(obj)
);
}
// export function findPossesiveToShrinkInVP(VP: T.VPRendered): T.Rendered<T.NPSelection>[] {
// const obj: T.Rendered<T.NPSelection> | undefined = ("object" in VP && typeof VP.object === "object")
// ? VP.object
// : undefined;
// return [
// ...findPossesivesInNP(VP.subject),
// ...findPossesivesInNP(obj),
// ];
// }
export function shrinkNP(np: T.Rendered<T.NPSelection>): Segment {
function getFirstSecThird(): 1 | 2 | 3 {

View File

@ -9,7 +9,7 @@ function getBaseAndAdjectives(np: T.Rendered<T.NPSelection | T.EqCompSelection>)
if (np.type === "sandwich") {
return getSandwichPsBaseAndAdjectives(np);
}
const adjs = np.adjectives;
const adjs = "adjectives" in np && np.adjectives;
if (!adjs) {
return np.ps;
}
@ -75,9 +75,20 @@ function trimOffShrunkenPossesive(p: T.Rendered<T.NPSelection>): T.Rendered<T.NP
export function getPashtoFromRendered(np: T.Rendered<T.NPSelection> | T.Rendered<T.EqCompSelection>, subjectsPerson: false | T.Person): T.PsString[] {
const base = getBaseAndAdjectives(np);
if (np.type === "loc. adv." || np.type === "adjective") {
if (np.type === "loc. adv.") {
return base;
}
if (np.type === "adjective") {
if (!np.sandwich) {
return base
}
const sandwichPs = getPashtoFromRendered(np.sandwich, false);
return base.flatMap(p => (
sandwichPs.flatMap(s => (
concatPsString(s, " ", p)
))
));
}
const trimmed = np.type === "sandwich" ? {
...np,
inside: trimOffShrunkenPossesive(np.inside),
@ -184,9 +195,12 @@ export function getEnglishFromRendered(r: T.Rendered<T.NPSelection | T.EqCompSel
return getEnglishFromRenderedSandwich(r);
}
if (!r.e) return undefined;
if (r.type === "loc. adv." || r.type === "adjective") {
if (r.type === "loc. adv.") {
return r.e;
}
if (r.type === "adjective") {
return getEnglishFromRenderedAdjective(r);
}
if (r.type !== "pronoun") {
// TODO: shouldn't have to do this 'as' - should be automatically narrowing
const np = r as T.Rendered<T.NounSelection>;
@ -201,3 +215,11 @@ function getEnglishFromRenderedSandwich(r: T.Rendered<T.SandwichSelection<T.Sand
if (!insideE) return undefined;
return `${r.e} ${insideE}`;
}
function getEnglishFromRenderedAdjective(a: T.Rendered<T.AdjectiveSelection>): string | undefined {
if (!a.sandwich) {
return a.e;
}
if (!a.e) return undefined;
return `${a.e} ${getEnglishFromRenderedSandwich(a.sandwich)}`;
}

View File

@ -9,6 +9,7 @@ import {
personGender,
personIsPlural,
} from "../../lib/misc-helpers";
import { renderSandwich } from "./render-sandwich";
function chooseInflection(inflections: T.UnisexSet<T.InflectionSet>, pers: T.Person, inflected?: boolean): T.ArrayOneOrMore<T.PsString> {
const gender = personGender(pers);
@ -30,8 +31,8 @@ export function renderAdjectiveSelection(a: T.AdjectiveSelection, person: T.Pers
entry: a.entry,
ps: [psStringFromEntry(a.entry)],
e,
inflected: false,
role,
inflected,
sandwich: a.sandwich ? renderSandwich(a.sandwich) : undefined,
person,
}
if (!infs.inflections || !isUnisexSet(infs.inflections)) {
@ -42,8 +43,8 @@ export function renderAdjectiveSelection(a: T.AdjectiveSelection, person: T.Pers
entry: a.entry,
ps: chooseInflection(infs.inflections, person, inflected),
e,
inflected: false,
role,
inflected,
person,
sandwich: a.sandwich ? renderSandwich(a.sandwich) : undefined,
};
}

View File

@ -641,6 +641,7 @@ export type AdverbSelection = {
export type AdjectiveSelection = {
type: "adjective",
entry: AdjectiveEntry,
sandwich: SandwichSelection<Sandwich> | undefined,
}
export type LocativeAdverbSelection = {
@ -679,6 +680,16 @@ export type Rendered<T extends NPSelection | EqCompSelection | AdjectiveSelectio
? Omit<SandwichSelection<Sandwich>, "inside"> & {
inside: Rendered<NPSelection>,
}
: T extends AdjectiveSelection
? {
type: "adjective",
entry: AdjectiveEntry,
ps: PsString[],
e?: string,
sandwich: Rendered<SandwichSelection<Sandwich>> | undefined,
inflected: boolean,
person: Person,
}
: ReplaceKey<
Omit<T, "changeGender" | "changeNumber" | "changeDistance" | "adjectives" | "possesor">,
"e",