sandwiches in eqcomplement and adjectives
This commit is contained in:
parent
e8ec806ecb
commit
5c9b7b7c3e
|
@ -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",
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
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}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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}
|
||||
/>}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>;
|
||||
|
@ -200,4 +214,12 @@ function getEnglishFromRenderedSandwich(r: T.Rendered<T.SandwichSelection<T.Sand
|
|||
const insideE = getEnglishFromRendered(r.inside);
|
||||
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)}`;
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
}
|
11
src/types.ts
11
src/types.ts
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue