Ability to use complements in the phrase builder with kawul/kedul stative! This is still a bit messy, and I should somehow get the "verbComplement" types deprecated

This commit is contained in:
lingdocs 2022-07-10 16:29:23 -05:00
parent 66fd580411
commit c3170f65f5
22 changed files with 359 additions and 136 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@lingdocs/pashto-inflector",
"version": "3.3.1",
"version": "3.3.2",
"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

@ -1,32 +1,37 @@
import { useState, useEffect } from "react";
import * as T from "../../../types";
import AdjectivePicker from "../../np-picker/AdjectivePicker";
import LocativeAdverbPicker from "./LocativeAdverbPicker";
import SandwichPicker from "../../np-picker/SandwichPicker";
const compTypes: T.EqCompType[] = ["adjective", "loc. adv.", "sandwich"];
import * as T from "../types";
import AdjectivePicker from "./np-picker/AdjectivePicker";
import LocativeAdverbPicker from "./ep-explorer/eq-comp-picker/LocativeAdverbPicker";
import SandwichPicker from "./np-picker/SandwichPicker";
const compTypes: T.ComplementType[] = ["adjective", "loc. adv.", "sandwich", "comp. noun"];
function EqCompPicker(props: {
function selectionTypeToCompType(s: Exclude<T.ComplementType, "comp. noun"> | "noun"): T.ComplementType {
if (s === "noun") return "comp. noun";
return s;
}
function ComplementPicker(props: {
phraseIsComplete: boolean,
onChange: (comp: T.EqCompSelection | undefined) => void,
comp: T.EqCompSelection | undefined,
onChange: (comp: T.ComplementSelection | undefined) => void,
comp: T.ComplementSelection | undefined,
opts: T.TextOptions,
cantClear?: boolean,
heading?: JSX.Element | string,
entryFeeder: T.EntryFeeder,
}) {
const [compType, setCompType] = useState<T.EqCompType | undefined>(props.comp
? props.comp.selection.type
const [compType, setCompType] = useState<T.ComplementType | undefined>(props.comp
? selectionTypeToCompType(props.comp.selection.type)
: undefined);
useEffect(() => {
setCompType(props.comp
? props.comp.selection.type
? selectionTypeToCompType(props.comp.selection.type)
: undefined);
}, [props.comp]);
function handleClear() {
setCompType(undefined);
props.onChange(undefined);
}
function handleCompTypeChange(ctp: T.EqCompType) {
function handleCompTypeChange(ctp: T.ComplementType) {
props.onChange(undefined);
setCompType(ctp);
}
@ -70,7 +75,7 @@ function EqCompPicker(props: {
entryFeeder={props.entryFeeder}
adjective={props.comp?.selection.type === "adjective" ? props.comp.selection : undefined}
opts={props.opts}
onChange={(a) => props.onChange(a ? { type: "EQComp", selection: a } : undefined)}
onChange={(a) => props.onChange(a ? { type: "complement", selection: a } : undefined)}
phraseIsComplete={props.phraseIsComplete}
/>
: compType === "loc. adv."
@ -78,11 +83,11 @@ function EqCompPicker(props: {
entryFeeder={props.entryFeeder.locativeAdverbs}
adjective={props.comp?.selection.type === "loc. adv." ? props.comp.selection : undefined}
opts={props.opts}
onChange={(a) => props.onChange(a ? { type: "EQComp", selection: a } : undefined)}
onChange={(a) => props.onChange(a ? { type: "complement", selection: a } : undefined)}
/>
: compType === "sandwich"
? <SandwichPicker
onChange={(a) => props.onChange(a ? { type: "EQComp", selection: a } : undefined)}
onChange={(a) => props.onChange(a ? { type: "complement", selection: a } : undefined)}
opts={props.opts}
sandwich={props.comp?.selection.type === "sandwich" ? props.comp.selection : undefined}
entryFeeder={props.entryFeeder}
@ -90,9 +95,13 @@ function EqCompPicker(props: {
// TODO: get phraseIsComplete working here
phraseIsComplete={props.phraseIsComplete}
/>
: compType === "comp. noun"
? <div style={{ maxWidth: "9rem" }}>
Sorry, can't choose complement nouns yet 🚧
</div>
: null}
</div>
</>;
}
export default EqCompPicker;
export default ComplementPicker;

View File

@ -30,8 +30,8 @@ function Block({ opts, block, king, script }: {
const english = getEnglishFromRendered(block.block.selection);
return <div className="text-center">
<div><strong>Predicate</strong></div>
{block.block.selection.type === "EQComp"
? <EqCompBlock opts={opts} comp={block.block.selection.selection} script={script} />
{block.block.selection.type === "complement"
? <ComplementBlock opts={opts} comp={block.block.selection.selection} script={script} />
: <NPBlock opts={opts} english={english} script={script}>{block.block.selection}</NPBlock>}
</div>
}
@ -63,6 +63,9 @@ function Block({ opts, block, king, script }: {
if (block.block.type === "modalVerbKedulPart") {
return <ModalAuxBlock opts={opts} aux={block.block} script={script} />
}
if (block.block.type === "complement") {
return <ComplementBlock opts={opts} comp={block.block.selection} script={script} />;
}
return null;
}
@ -99,7 +102,12 @@ function VerbSBlock({ opts, v, script }: {
return <div className="text-center">
{"long" in v.ps && <div className="clickable small mb-1" onClick={changeLength}>{length}</div>}
<Border>
<>
{v.type === "verb" && v.complement && <span className="mx-2">
<ComplementBlock opts={opts} comp={v.complement.selection} script={script} inside />
</span>}
{getLength(v.ps, length)[0][script]}
</>
</Border>
<div>{v.type === "perfectParticipleBlock" ? "Past Partic." : "Verb"}</div>
<EnglishBelow>{((v.type === "perfectParticipleBlock" ? "Past Partic." : "Verb")
@ -244,10 +252,11 @@ function ObjectBlock({ opts, obj, role, script }: {
</div>;
}
function EqCompBlock({ opts, comp, script }: {
function ComplementBlock({ opts, comp, script, inside }: {
script: "p" | "f",
opts: T.TextOptions,
comp: T.Rendered<T.EqCompSelection["selection"]>,
comp: T.Rendered<T.ComplementSelection["selection"]> | T.Rendered<T.UnselectedComplementSelection>["selection"],
inside?: boolean,
}) {
function AdjectiveBlock({ opts, adj }: {
opts: T.TextOptions,
@ -274,18 +283,31 @@ function EqCompBlock({ opts, comp, script }: {
<EnglishBelow>{adv.e}</EnglishBelow>
</div>;
}
return <div className="text-center">
<div>Comp.</div>
{comp.type === "adjective"
? <AdjectiveBlock opts={opts} adj={comp} />
: comp.type === "loc. adv."
? <LocAdvBlock opts={opts} adv={comp} />
: <div>
<Sandwich opts={opts} sandwich={comp} script={script} />
<div>Sandwich</div>
? <LocAdvBlock opts={opts} adv={comp} />
: comp.type === "noun"
? <Border>
NOT DONE YET
</Border>
: comp.type === "unselected"
? <div>
<Border>
____
</Border>
{!inside && <>
<div>&nbsp;</div>
<EnglishBelow>{comp.e}</EnglishBelow>
</div>}
</>}
</div>
: <div>
<Sandwich opts={opts} sandwich={comp} script={script} />
<div>Sandwich</div>
<EnglishBelow>{comp.e}</EnglishBelow>
</div>}
</div>;
}

View File

@ -2,7 +2,7 @@ import * as T from "../../types";
import NPPicker from "../np-picker/NPPicker";
import EquativePicker from "./EquativePicker";
import ButtonSelect from "../ButtonSelect";
import EqCompPicker from "./eq-comp-picker/EqCompPicker";
import ComplementPicker from "../ComplementPicker";
import epsReducer, { EpsReducerAction } from "./eps-reducer";
import { useEffect, useRef, useState } from "react";
import { completeEPSelection } from "../../lib/phrase-building/render-ep";
@ -92,10 +92,10 @@ function EPPicker({ opts, eps, onChange, entryFeeder }: {
role="subject"
onChange={payload => adjustEps({ type: "set predicate NP", payload })}
opts={opts}
/> : <EqCompPicker
/> : <ComplementPicker
phraseIsComplete={phraseIsComplete}
comp={eps.predicate.type === "Complement" ? eps.predicate.Complement : undefined}
onChange={payload => adjustEps({ type: "set predicate comp", payload })}
onChange={payload => adjustEps({ type: "set predicate complement", payload })}
opts={opts}
entryFeeder={entryFeeder}
/>}

View File

@ -17,8 +17,8 @@ export type EpsReducerAction = {
type: "set predicate NP",
payload: T.NPSelection | undefined,
} | {
type: "set predicate comp",
payload: T.EqCompSelection | undefined,
type: "set predicate complement",
payload: T.ComplementSelection | undefined,
} | {
type: "set omitSubject",
payload: "true" | "false",
@ -134,7 +134,7 @@ export default function epsReducer(eps: T.EPSelectionState, action: EpsReducerAc
};
return selection ? ensureMiniPronounsOk(eps, n, sendAlert) : n;
}
if (action.type === "set predicate comp") {
if (action.type === "set predicate complement") {
return {
...eps,
predicate: {

View File

@ -486,6 +486,7 @@ function getRandomVPSelection(mix: MixType = "both") {
)),
verb: randomizeTense(verb, true),
form: { removeKing: false, shrinkServant: false },
externalComplement: undefined,
}
}
return {
@ -501,6 +502,7 @@ function getRandomVPSelection(mix: MixType = "both") {
)),
verb: randomizeTense(verb, true),
form: { removeKing: false, shrinkServant: false },
externalComplement: undefined,
};
};
};

View File

@ -8,7 +8,13 @@ import VPExplorerExplanationModal, { roleIcon } from "./VPExplorerExplanationMod
import { vpsReducer, VpsReducerAction } from "./vps-reducer";
import APPicker from "../ap-picker/APPicker";
import autoAnimate from "@formkit/auto-animate";
import { getObjectSelection, getSubjectSelection, includesShrunkenServant, isNoObject } from "../../lib/phrase-building/blocks-utils";
import {
getObjectSelection,
getSubjectSelection,
includesShrunkenServant,
isNoObject,
} from "../../lib/phrase-building/blocks-utils";
import ComplementPicker from "../ComplementPicker";
function VPPicker({ opts, vps, onChange, entryFeeder }: {
opts: T.TextOptions,
@ -156,11 +162,6 @@ function VPPicker({ opts, vps, onChange, entryFeeder }: {
{!servantIsShrunk ? "🪄" : "👶"}
</span>
}
{(rendered && rendered.whatsAdjustable !== "servant") &&
<span onClick={() => adjustVps({ type: "toggle king remove" })} className="mx-2 clickable">
{!VPS?.form.removeKing ? "🚫" : "🙈"}
</span>
}
</div>}
entryFeeder={entryFeeder}
role="object"
@ -175,6 +176,19 @@ function VPPicker({ opts, vps, onChange, entryFeeder }: {
: null}
</div>;
})}
{vps.externalComplement && <div className="my-2 card block-card p-1 mr-1" key="complementPicker">
<div className="h5 text-center">Complement</div>
<ComplementPicker
phraseIsComplete={phraseIsComplete}
comp={vps.externalComplement.selection.type === "unselected"
? undefined
: vps.externalComplement as T.ComplementSelection // TODO: just typescript being dumb? - looks like it
}
onChange={payload => adjustVps({ type: "set externalComplement", payload })}
opts={opts}
entryFeeder={entryFeeder}
/>
</div>}
<div className="my-2">
<TensePicker
vps={vps}

View File

@ -71,10 +71,23 @@ export function makeVPSelectionState(
canChangeVoice: transitivity === "transitive",
canChangeStatDyn: "stative" in info,
},
externalComplement: takesExternalComplement(verb)
? { type: "complement", selection: { type: "unselected" }}
: undefined,
form: os ? os.form : { removeKing: false, shrinkServant: false },
};
}
function takesExternalComplement(v: T.VerbEntry): boolean {
if (v.entry.p === "کول" && v.entry.e.includes("to make")) {
return true;
}
if (v.entry.p === "کېدل" && v.entry.e.includes("to become")) {
return true;
}
return false;
}
export function changeStatDyn(v: T.VPSelectionState, s: "dynamic" | "stative"): T.VPSelectionState {
const info = getVerbInfo(v.verb.verb.entry, v.verb.verb.complement);
if (!("stative" in info)) {

View File

@ -74,7 +74,10 @@ export type VpsReducerAction = {
index: number,
direction: "back" | "forward",
},
};
} | {
type: "set externalComplement",
payload: T.ComplementSelection | undefined,
}
export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, sendAlert?: (msg: string) => void): T.VPSelectionState {
return ensureMiniPronounsOk(vps, doReduce());
@ -291,6 +294,18 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
blocks: shiftBlock(vps.blocks, index, direction),
};
}
if (action.type === "set externalComplement") {
const selection = action.payload;
return {
...vps,
externalComplement: selection === undefined
// TODO: this is a bit messy
// when using the ComplementPicker with an EP - undefined means it hasn't been selected
// when using the ComplementPicker with a VP - undefined means there can be no complement
? { type: "complement", selection: { type: "unselected" }}
: selection,
}
}
throw new Error("unknown vpsReducer state");
}
}

View File

@ -99,7 +99,18 @@ export function getPredicateSelectionFromBlocks(blocks: T.Block[][]): T.Rendered
export function getAPsFromBlocks(blocks: T.Block[][]): T.Rendered<T.APSelection>[] {
return blocks[0].filter(b => b.block.type === "AP").map(b => b.block) as T.Rendered<T.APSelection>[];
}
}
export function getComplementFromBlocks(blocks: T.Block[][]): T.Rendered<T.ComplementSelection> | T.Rendered<T.UnselectedComplementSelection> | undefined {
const complement = blocks[0].find(b => b.block.type === "complement");
if (!complement) {
return undefined;
}
if (complement.block.type !== "complement") {
throw new Error("error finding complement - other kind of block retrieved");
}
return complement.block;
}
export function getObjectSelection(blocks: T.VPSBlockComplete[]): T.ObjectSelectionComplete;
export function getObjectSelection(blocks: T.VPSBlock[]): T.ObjectSelection;
@ -256,7 +267,11 @@ export function removeAP<B extends T.VPSBlock[] | T.EPSBlock[]>(blocks: B, index
}
export function isNoObject(b: T.VPSBlock["block"] | T.EPSBlock["block"]): b is { type: "objectSelection", selection: "none" } {
return !!(b && b.type === "objectSelection" && b.selection === "none");
return !!(
b
&&
(b.type === "objectSelection" && b.selection === "none")
);
}
export function specifyEquativeLength(blocksWVars: T.Block[][], length: "long" | "short"): T.Block[][] {

View File

@ -14,12 +14,12 @@ import { completeVPSelection } from "./vp-tools";
import { renderVP } from "./render-vp";
import {
getAPsFromBlocks,
getComplementFromBlocks,
getObjectSelectionFromBlocks,
getPredicateSelectionFromBlocks,
getSubjectSelectionFromBlocks,
getVerbFromBlocks,
hasEquativeWithLengths,
hasVerbWithLengths,
specifyEquativeLength,
specifyVerbLength,
} from "./blocks-utils";
@ -81,10 +81,14 @@ export function compileVP(VP: T.VPRendered, form: T.FormVersion, combineLengths?
}
function compileVPPs(blocks: T.Block[][], kids: T.Kid[], form: T.FormVersion, king: "subject" | "object"): T.SingleOrLengthOpts<T.PsString[]> {
if (hasVerbWithLengths(blocks)) {
const verbBlock = getVerbFromBlocks(blocks);
if ("long" in verbBlock.block.ps) {
return {
long: compileVPPs(specifyVerbLength(blocks, "long"), kids, form, king) as T.PsString[],
short: compileVPPs(specifyVerbLength(blocks, "short"), kids, form, king) as T.PsString[],
..."mini" in verbBlock.block.ps ? {
mini: compileVPPs(specifyVerbLength(blocks, "mini"), kids, form, king) as T.PsString[],
} : {},
};
}
const subjectPerson = getSubjectSelectionFromBlocks(blocks)
@ -94,6 +98,7 @@ function compileVPPs(blocks: T.Block[][], kids: T.Kid[], form: T.FormVersion, ki
kids,
false,
);
console.log({ blocksWKids });
return removeDuplicates(combineIntoText(blocksWKids, subjectPerson, {}));
}
@ -198,7 +203,7 @@ function getPsFromPiece(piece: T.Block | T.Kid, subjectPerson: T.Person): T.PsSt
return [piece.block.ps];
}
if (piece.block.type === "verbComplement") {
return [{ p: "---", f: "---"}]; //getPashtoFromRendered(piece.block.complement);
return [{ p: "____", f: "____"}]; //getPashtoFromRendered(piece.block.complement);
}
if (piece.block.type === "objectSelection") {
if (typeof piece.block.selection !== "object") {
@ -208,7 +213,11 @@ function getPsFromPiece(piece: T.Block | T.Kid, subjectPerson: T.Person): T.PsSt
}
if (piece.block.type === "verb") {
// getLong is just for type safety - we will have split up the length options earlier in compileVPPs
return getLong(piece.block.block.ps);
const verbPs = getLong(piece.block.block.ps);
if (piece.block.block.complement) {
return combineComplementWVerbPs(piece.block.block.complement, verbPs);
}
return verbPs;
}
if (piece.block.type === "perfectParticipleBlock") {
// getLong is just for type safety - we will have split up the length options earlier in compileVPPs
@ -226,6 +235,13 @@ function getPsFromPiece(piece: T.Block | T.Kid, subjectPerson: T.Person): T.PsSt
// just using the short one for now - it will only be short anyways
return getShort(piece.block.ps);
}
if (piece.block.type === "complement") {
if (piece.block.selection.type === "sandwich") {
// TODO: Kinda cheating
return getPashtoFromRendered({ type: "AP", selection: piece.block.selection }, false);
}
return piece.block.selection.ps;
}
}
if ("kid" in piece) {
if (piece.kid.type === "ba") {
@ -235,10 +251,18 @@ function getPsFromPiece(piece: T.Block | T.Kid, subjectPerson: T.Person): T.PsSt
return [piece.kid.ps];
}
}
throw new Error("unrecognized piece type");
}
function combineComplementWVerbPs(comp: T.Rendered<T.ComplementSelection | T.UnselectedComplementSelection>, v: T.PsString[]): T.PsString[] {
const compPs = comp.selection.type === "sandwich"
? getPashtoFromRendered({ type: "AP", selection: comp.selection }, false)
: comp.selection.ps;
return compPs.flatMap((c) => (
v.map((p) => concatPsString(c, " ", p))
));
}
function getEngAPs(blocks: T.Block[][]): string {
return getAPsFromBlocks(blocks).reduce((accum, curr) => {
const e = getEnglishFromRendered(curr);
@ -247,6 +271,17 @@ function getEngAPs(blocks: T.Block[][]): string {
}, "");
}
function getEngComplement(blocks: T.Block[][]): string | undefined {
const comp = getComplementFromBlocks(blocks);
if (!comp) return undefined;
if (comp.selection === undefined) {
return "____";
}
if (comp.selection.type === "sandwich") {
return getEnglishFromRendered({ type: "AP", selection: comp.selection });
}
}
function putKidsInKidsSection(blocksWVars: T.Block[][], kids: T.Kid[], enforceKidsSectionBlankout: boolean): (T.Block | T.Kid | T.PsString)[][] {
function insert(blocks: T.Block[]): (T.Block | T.Kid | T.PsString)[] {
const first = blocks[0];
@ -261,8 +296,13 @@ function putKidsInKidsSection(blocksWVars: T.Block[][], kids: T.Kid[], enforceKi
}
function compileEnglishVP(VP: T.VPRendered): string[] | undefined {
function insertEWords(e: string, { subject, object, APs }: { subject: string, object?: string, APs: string }): string {
return e.replace("$SUBJ", subject).replace("$OBJ", object || "") + APs;
function insertEWords(e: string, { subject, object, APs, complement }: { subject: string, object?: string, APs: string, complement: string | undefined }): string {
return e
.replace("$SUBJ", subject)
.replace("$OBJ", object || "")
// TODO: check if this always works
+ (complement ? ` ${complement}` : "")
+ APs;
}
const engSubj = getSubjectSelectionFromBlocks(VP.blocks).selection;
const obj = getObjectSelectionFromBlocks(VP.blocks).selection;
@ -272,6 +312,7 @@ function compileEnglishVP(VP: T.VPRendered): string[] | undefined {
? ""
: undefined;
const engAPs = getEngAPs(VP.blocks);
const engComplement = getEngComplement(VP.blocks);
// require all English parts for making the English phrase
return (VP.englishBase && engSubj && engObj !== undefined)
? VP.englishBase.map(e => insertEWords(e, {
@ -279,6 +320,7 @@ function compileEnglishVP(VP: T.VPRendered): string[] | undefined {
subject: getEnglishFromRendered(engSubj) || "",
object: engObj ? getEnglishFromRendered(engObj) : "",
APs: engAPs,
complement: engComplement,
})).map(capitalizeFirstLetter)
: undefined;
}

View File

@ -41,9 +41,6 @@ export function renderEnglishVPBase({ subjectPerson, object, vs }: {
function isToBe(v: T.EnglishVerbConjugationEc): boolean {
return (v[2] === "being");
}
const futureEngBuilder: T.EnglishBuilder = (s: T.Person, ec: T.EnglishVerbConjugationEc, n: boolean) => ([
`$SUBJ will${n ? " not" : ""} ${isToBe(ec) ? "be" : ec[0]}`,
]);
// TODO: Pull these out to a seperate entity and import it
const basicBuilders: Record<
T.VerbTense,
@ -59,8 +56,13 @@ export function renderEnglishVPBase({ subjectPerson, object, vs }: {
`that $SUBJ ${n ? " won't" : " will"} ${isToBe(ec) ? "be" : ec[0]}`,
`$SUBJ ${n ? " not" : ""} should ${isToBe(ec) ? "be" : ec[0]}`,
]),
imperfectiveFuture: futureEngBuilder,
perfectiveFuture: futureEngBuilder,
imperfectiveFuture: (s: T.Person, ec: T.EnglishVerbConjugationEc, n: boolean) => ([
`$SUBJ will${n ? " not" : ""} ${isToBe(ec) ? "be" : ec[0]}`,
`$SUBJ will${n ? " not" : ""} be ${isToBe(ec) ? "be" : ec[2]}`,
]),
perfectiveFuture: (s: T.Person, ec: T.EnglishVerbConjugationEc, n: boolean) => ([
`$SUBJ will${n ? " not" : ""} ${isToBe(ec) ? "be" : ec[0]}`,
]),
imperfectivePast: (s: T.Person, ec: T.EnglishVerbConjugationEc, n: boolean) => ([
// - subj pastEquative (N && "not") ec.2 obj
`$SUBJ ${engEquative("past", s)}${n ? " not" : ""} ${ec[2]}`,

View File

@ -5,7 +5,7 @@ import {
import * as T from "../../types";
import { concatPsString } from "../p-text-helpers";
function getBaseAndAdjectives({ selection }: T.Rendered<T.NPSelection | T.EqCompSelection | T.APSelection>): T.PsString[] {
function getBaseAndAdjectives({ selection }: T.Rendered<T.NPSelection | T.ComplementSelection | T.APSelection>): T.PsString[] {
if (selection.type === "sandwich") {
return getSandwichPsBaseAndAdjectives(selection);
}
@ -79,7 +79,7 @@ function trimOffShrunkenPossesive(p: T.Rendered<T.NPSelection>): T.Rendered<T.NP
};
}
export function getPashtoFromRendered(b: T.Rendered<T.NPSelection> | T.Rendered<T.EqCompSelection> | T.Rendered<T.APSelection>, subjectsPerson: false | T.Person): T.PsString[] {
export function getPashtoFromRendered(b: T.Rendered<T.NPSelection> | T.Rendered<T.ComplementSelection> | T.Rendered<T.APSelection>, subjectsPerson: false | T.Person): T.PsString[] {
const base = getBaseAndAdjectives(b);
if (b.selection.type === "loc. adv." || b.selection.type === "adverb") {
return base;
@ -88,6 +88,7 @@ export function getPashtoFromRendered(b: T.Rendered<T.NPSelection> | T.Rendered<
if (!b.selection.sandwich) {
return base
}
// TODO: Kinda cheating
const sandwichPs = getPashtoFromRendered({ type: "AP", selection: b.selection.sandwich }, false);
return base.flatMap(p => (
sandwichPs.flatMap(s => (
@ -199,7 +200,10 @@ function pronounPossEng(p: T.Person): string {
return "their";
}
export function getEnglishFromRendered(r: T.Rendered<T.NPSelection | T.EqCompSelection | T.APSelection>): string | undefined {
export function getEnglishFromRendered(r: T.Rendered<T.NPSelection | T.ComplementSelection | T.APSelection | T.SandwichSelection<T.Sandwich>>): string | undefined {
if (r.type === "sandwich") {
return getEnglishFromRenderedSandwich(r);
}
if (r.selection.type === "sandwich") {
return getEnglishFromRenderedSandwich(r.selection);
}

View File

@ -18,30 +18,29 @@ function chooseInflection(inflections: T.UnisexSet<T.InflectionSet>, pers: T.Per
return inflections[gender][infNumber];
}
export function renderAdjectiveSelection(a: T.AdjectiveSelection, person: T.Person, inflected: boolean, role: "king" | "servant" | "none"): T.Rendered<T.AdjectiveSelection> {
export function inflectAdjective(a: T.AdjectiveSelection, person: T.Person, inflected: boolean): T.ArrayOneOrMore<T.PsString> {
const infs = inflectWord(a.entry);
if (!infs) {
return [psStringFromEntry(a.entry)];
}
if (!infs.inflections || !isUnisexSet(infs.inflections)) {
throw new Error("error getting inflections for adjective, looks like a noun's inflections");
}
return chooseInflection(infs.inflections, person, inflected);
}
export function renderAdjectiveSelection(a: T.AdjectiveSelection, person: T.Person, inflected: boolean): T.Rendered<T.AdjectiveSelection> {
const eWord = getEnglishWord(a.entry);
const e = !eWord
? undefined
: typeof eWord === "string"
? eWord
: (eWord.singular || undefined);
if (!infs) return {
type: "adjective",
entry: a.entry,
ps: [psStringFromEntry(a.entry)],
e,
inflected,
sandwich: a.sandwich ? renderSandwich(a.sandwich) : undefined,
person,
}
if (!infs.inflections || !isUnisexSet(infs.inflections)) {
throw new Error("error getting inflections for adjective, looks like a noun's inflections");
}
const ps = inflectAdjective(a, person, inflected);
return {
type: "adjective",
entry: a.entry,
ps: chooseInflection(infs.inflections, person, inflected),
ps,
e,
inflected,
person,

View File

@ -32,7 +32,7 @@ export function findPossesivesToShrink(
];
}
if (block.type === "predicateSelection") {
if (block.selection.type === "EQComp") {
if (block.selection.type === "complement") {
if (block.selection.selection.type === "sandwich") {
return [
...kids,

View File

@ -0,0 +1,55 @@
import * as T from "../../types";
import { renderNounSelection } from "./render-np";
import { getEnglishWord } from "../get-english-word";
import { psStringFromEntry } from "../p-text-helpers";
import { renderAdjectiveSelection } from "./render-adj";
import { renderSandwich } from "./render-sandwich";
export function renderComplementSelection(s: T.ComplementSelection | T.UnselectedComplementSelection, person: T.Person): T.Rendered<T.ComplementSelection | T.UnselectedComplementSelection> {
if (s.selection.type === "unselected") {
return {
type: "complement",
selection: {
type: "unselected",
ps: [{ p: "____", f: "____" }],
e: "____",
},
};
}
if (s.selection.type === "sandwich") {
return {
type: "complement",
selection: renderSandwich(s.selection),
};
}
const e = getEnglishWord(s.selection.entry);
if (!e || typeof e !== "string") {
throw new Error("error getting english for compliment");
}
if (s.selection.type === "loc. adv.") {
return {
type: "complement",
selection: {
type: "loc. adv.",
entry: s.selection.entry,
ps: [psStringFromEntry(s.selection.entry)],
e,
inflected: false,
// TODO: don't use persons for these
person,
role: "none",
},
};
}
if (s.selection.type === "adjective") {
return {
type: "complement",
selection: renderAdjectiveSelection(s.selection, person, false),
};
}
// if (s.selection.type === "noun") {
return {
type: "complement",
selection: renderNounSelection(s.selection, false, "none"),
};
}

View File

@ -8,12 +8,11 @@ import { getPersonFromVerbForm } from "../../lib/misc-helpers";
import { getVerbBlockPosFromPerson } from "../misc-helpers";
import { getEnglishWord } from "../get-english-word";
import { psStringFromEntry } from "../p-text-helpers";
import { isLocativeAdverbEntry } from "../type-predicates";
import { renderAdjectiveSelection } from "./render-adj";
import { renderSandwich } from "./render-sandwich";
import { EPSBlocksAreComplete, getSubjectSelection, makeBlock, makeKid } from "./blocks-utils";
import { removeAccentsWLength } from "../accent-helpers";
import { findPossesivesToShrink, orderKids } from "./render-common";
import { renderComplementSelection } from "./render-complement";
export function renderEP(EP: T.EPSelectionComplete): T.EPRendered {
const { kids, blocks, englishEquativePerson } = getEPSBlocksAndKids(EP);
@ -45,7 +44,8 @@ function getEPSBlocksAndKids(EP: T.EPSelectionComplete): { kids: T.Kid[], blocks
type: "predicateSelection",
selection: EP.predicate.selection.type === "NP"
? renderNPSelection(EP.predicate.selection, false, false, "subject", "king")
: renderEqCompSelection(EP.predicate.selection, commandingPerson),
// we won't have an unselected complement in the EP - TODO: make safer?
: renderComplementSelection(EP.predicate.selection, commandingPerson) as T.Rendered<T.ComplementSelection>,
}),
makeBlock(equative),
], EP.equative.negative);
@ -150,40 +150,6 @@ export function renderAdverbSelection(a: T.AdverbSelection): T.Rendered<T.Adverb
};
}
function renderEqCompSelection(s: T.EqCompSelection, person: T.Person): T.Rendered<T.EqCompSelection> {
if (s.selection.type === "sandwich") {
return {
type: "EQComp",
selection: renderSandwich(s.selection),
};
}
const e = getEnglishWord(s.selection.entry);
if (!e || typeof e !== "string") {
throw new Error("error getting english for compliment");
}
if (isLocativeAdverbEntry(s.selection.entry)) {
return {
type: "EQComp",
selection: {
type: "loc. adv.",
entry: s.selection.entry,
ps: [psStringFromEntry(s.selection.entry)],
e,
inflected: false,
person,
role: "none",
},
};
}
if (s.selection.type === "adjective") {
return {
type: "EQComp",
selection: renderAdjectiveSelection(s.selection, person, false, "none"),
};
}
throw new Error("invalid EqCompSelection");
}
const equativeBuilders: Record<T.EquativeTense, (p: T.Person, n: boolean) => string[]> = {
present: (p, n) => {
return [

View File

@ -46,7 +46,7 @@ export function renderNPSelection(NP: T.NPSelection, inflected: boolean, inflect
throw new Error("unknown NP type");
};
function renderNounSelection(n: T.NounSelection, inflected: boolean, role: "servant" | "king" | "none"): T.Rendered<T.NounSelection> {
export function renderNounSelection(n: T.NounSelection, inflected: boolean, role: "servant" | "king" | "none"): T.Rendered<T.NounSelection> {
const english = getEnglishFromNoun(n.entry, n.number);
const pashto = ((): T.PsString[] => {
const infs = inflectWord(n.entry);
@ -64,7 +64,7 @@ function renderNounSelection(n: T.NounSelection, inflected: boolean, role: "serv
const person = getPersonNumber(n.gender, n.number);
return {
...n,
adjectives: n.adjectives.map(a => renderAdjectiveSelection(a, person, inflected, role)),
adjectives: n.adjectives.map(a => renderAdjectiveSelection(a, person, inflected)),
person,
inflected,
role,

View File

@ -29,6 +29,7 @@ import { renderNPSelection } from "./render-np";
import { findPerfectiveHead, getObjectSelection, getSubjectSelection, makeBlock, makeKid } from "./blocks-utils";
import { renderAPSelection } from "./render-ap";
import { findPossesivesToShrink, orderKids, getMiniPronounPs } from "./render-common";
import { renderComplementSelection } from "./render-complement";
// TODO: ISSUE GETTING SPLIT HEAD NOT MATCHING WITH FUTURE VERBS
@ -42,6 +43,7 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
const kingPerson = getPersonFromNP(
king === "subject" ? subject : object,
);
const complementPerson = getPersonFromNP(object ? object : subject)
// TODO: more elegant way of handling this type safety
if (kingPerson === undefined) {
throw new Error("king of sentance does not exist");
@ -56,8 +58,9 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
inflectSubject,
inflectObject,
king,
complementPerson,
});
const { verbBlocks, hasBa } = renderVerbSelection(VP.verb, kingPerson, objectPerson);
const { verbBlocks, hasBa } = renderVerbSelection(VP.verb, kingPerson, objectPerson, VP.externalComplement);
const b: T.VPRendered = {
type: "VPRendered",
king,
@ -332,6 +335,7 @@ function renderVPBlocks(blocks: T.VPSBlockComplete[], config: {
inflectSubject: boolean,
inflectObject: boolean,
king: "subject" | "object",
complementPerson: T.Person | undefined,
}): T.Block[] {
return blocks.reduce((blocks, { block }): T.Block[] => {
if (block.type === "subjectSelection") {
@ -363,9 +367,20 @@ function renderVPBlocks(blocks: T.VPSBlockComplete[], config: {
}),
];
}
if (block.type === "AP") {
return [
...blocks,
makeBlock(renderAPSelection(block)),
];
}
return [
...blocks,
makeBlock(renderAPSelection(block)),
makeBlock(
renderComplementSelection(
block,
// just for typesafety // TODO: only include the person if we're doing an adjective
config.complementPerson || T.Person.FirstSingMale,
)),
];
}, [] as T.Block[]);
}
@ -390,7 +405,7 @@ type VerbBlocks =
| [T.PerfectParticipleBlock, T.PerfectEquativeBlock] // perfect verb
| [T.ModalVerbBlock, T.ModalVerbKedulPart] // modal verb
function renderVerbSelection(vs: T.VerbSelectionComplete, person: T.Person, objectPerson: T.Person | undefined): {
function renderVerbSelection(vs: T.VerbSelectionComplete, person: T.Person, complementPerson: T.Person | undefined, externalComplement: T.ComplementSelection | T.UnselectedComplementSelection | undefined): {
verbBlocks: VerbBlocks
hasBa: boolean,
} {
@ -404,7 +419,9 @@ function renderVerbSelection(vs: T.VerbSelectionComplete, person: T.Person, obje
: "stative" in conjugations
? conjugations.stative
: conjugations;
const { ps: { head, rest }, hasBa } = getPsVerbConjugation(conj, vs, person, objectPerson);
const { ps: { head, rest }, hasBa } = getPsVerbConjugation(conj, vs, person, complementPerson);
const perfective = isPerfective(vs.tense);
const stativeHelper = isStativeHelper(vs.verb)
const vrb: T.VerbRenderedBlock = {
type: "verb",
block: {
@ -412,11 +429,14 @@ function renderVerbSelection(vs: T.VerbSelectionComplete, person: T.Person, obje
ps: rest,
person,
hasBa,
complement: (!perfective && stativeHelper && externalComplement)
? renderComplementSelection(externalComplement, complementPerson || T.Person.FirstSingMale)
: undefined,
},
};
const verbBlocks = [
...(head ? (
vs.isCompound === "stative" ? [{
(vs.isCompound === "stative" && !vrb.block.complement) ? [{
type: "verbComplement",
complement: head,
} as T.VerbComplementBlock] : [{
@ -424,6 +444,9 @@ function renderVerbSelection(vs: T.VerbSelectionComplete, person: T.Person, obje
ps: head,
} as T.PerfectiveHeadBlock]
) : [] as [T.VerbComplementBlock] | [T.PerfectiveHeadBlock] | []),
...(externalComplement && perfective && stativeHelper)
? [renderComplementSelection(externalComplement, complementPerson || T.Person.FirstSingMale)]
: [],
...splitUpIfModal(vrb),
] as VerbBlocks;
const perfectStuff = isPerfectTense(vrb.block.tense) ? getPerfectStuff(rest, vrb) : undefined;
@ -433,6 +456,12 @@ function renderVerbSelection(vs: T.VerbSelectionComplete, person: T.Person, obje
};
}
function isStativeHelper(v: T.VerbEntry): boolean {
if (v.entry.p === "کول" && v.entry.e.includes("make")) return true;
if (v.entry.p === "کېدل" && v.entry.e.includes("become")) return true;
return false;
}
function splitUpIfModal(v: T.VerbRenderedBlock): [T.VerbRenderedBlock] | [T.ModalVerbBlock, T.ModalVerbKedulPart] {
if (!isModalTense(v.block.tense)) {
return [v];
@ -489,7 +518,7 @@ function getPsVerbConjugation(conj: T.VerbConjugation, vs: T.VerbSelectionComple
if (perfective) {
const past = isPastTense(vs.tense);
const splitInfo = conj.info[(past || isModalTense(vs.tense)) ? "root" : "stem"].perfectiveSplit;
if (!splitInfo) return { ps: { head: undefined, rest: verbForm }, hasBa };
if (!splitInfo) return { ps: { head: undefined, rest: removeBaFromForm(verbForm) }, hasBa };
// TODO: Either solve this in the inflector or here, it seems silly (or redundant)
// to have a length option in the perfective split stem??
const [splitHead] = getLong(getMatrixBlock(splitInfo, objectPerson, person));

View File

@ -554,7 +554,7 @@ export type SubjectSelectionComplete = {
export type PredicateSelectionComplete = {
type: "predicateSelection",
selection: EqCompSelection | NPSelection,
selection: ComplementSelection | NPSelection,
};
export type ObjectSelectionComplete = {
@ -565,12 +565,14 @@ export type ObjectSelectionComplete = {
export type VPSelectionState = {
blocks: VPSBlock[]
verb: VerbSelection,
externalComplement: undefined | UnselectedComplementSelection | ComplementSelection,
form: FormVersion,
};
export type VPSelectionComplete = {
blocks: VPSBlockComplete[]
verb: VerbSelectionComplete,
externalComplement: VPSelectionState["externalComplement"],
form: FormVersion,
};
@ -690,19 +692,23 @@ export type RenderedPossesorSelection = {
shrunken: boolean,
};
export type UnselectedComplementSelection = { type: "complement", selection: { type: "unselected" }};
export type Rendered<
T extends
| NPSelection
| NPSelection["selection"]
| APSelection
| APSelection["selection"]
| EqCompSelection
| EqCompSelection["selection"]
| SubjectSelectionComplete
| ObjectSelectionComplete
| PredicateSelectionComplete
| AdjectiveSelection
| SandwichSelection<Sandwich>
| SandwichSelection<Sandwich>
| ComplementSelection
| ComplementSelection["selection"]
| UnselectedComplementSelection
| undefined
> =
T extends NPSelection
? {
@ -714,10 +720,19 @@ export type Rendered<
type: "AP",
selection: Rendered<APSelection["selection"]>
}
: T extends EqCompSelection
: T extends ComplementSelection
? {
type: "EQComp",
selection: Rendered<EqCompSelection["selection"]>
type: "complement",
selection: Rendered<ComplementSelection["selection"]>
}
: T extends UnselectedComplementSelection
? {
type: "complement",
selection: {
type: "unselected",
ps: PsString[],
e: string,
},
}
: T extends SandwichSelection<Sandwich>
? Omit<SandwichSelection<Sandwich>, "inside"> & {
@ -740,6 +755,11 @@ export type Rendered<
inflected: boolean,
person: Person,
}
: T extends ComplementSelection
? {
type: "complement",
selection: Rendered<ComplementSelection["selection"]>,
}
: T extends SubjectSelectionComplete
? {
type: "subjectSelection",
@ -753,8 +773,13 @@ export type Rendered<
: T extends PredicateSelectionComplete
? {
type: "predicateSelection",
selection: Rendered<EqCompSelection> | Rendered<NPSelection>,
} : ReplaceKey<
selection: Rendered<ComplementSelection> | Rendered<NPSelection>,
} : T extends undefined
? {
type: "undefined",
ps: PsString,
}
: ReplaceKey<
Omit<T, "changeGender" | "changeNumber" | "changeDistance" | "adjectives" | "possesor">,
"e",
string
@ -777,7 +802,7 @@ export type EPSelectionState = {
predicate: {
type: "NP" | "Complement",
NP: NPSelection | undefined,
Complement: EqCompSelection | undefined,
Complement: ComplementSelection | undefined,
},
equative: EquativeSelection,
omitSubject: boolean,
@ -795,11 +820,11 @@ export type EPSBlockComplete = {
export type VPSBlock = {
key: number,
// TODO: confusing use of APSelection / should be like APSelection s APSelection complete like the others
block: SubjectSelection | ObjectSelection | (APSelection | undefined),
block: SubjectSelection | ObjectSelection | (APSelection | undefined) | ComplementSelection,
};
export type VPSBlockComplete = {
key: number,
block: SubjectSelectionComplete | ObjectSelectionComplete | APSelection,
block: SubjectSelectionComplete | ObjectSelectionComplete | APSelection | ComplementSelection,
};
export type EPSelectionComplete = Omit<EPSelectionState, "predicate" | "blocks"> & {
@ -808,16 +833,20 @@ export type EPSelectionComplete = Omit<EPSelectionState, "predicate" | "blocks">
omitSubject: boolean,
};
export type EqCompType = "adjective" | "loc. adv." | "sandwich"
export type EqCompSelection = {
type: "EQComp",
selection: AdjectiveSelection | LocativeAdverbSelection | SandwichSelection<Sandwich>,
};
export type ComplementType = "adjective" | "loc. adv." | "sandwich" | "comp. noun";
export type SandwichSelection<S extends Sandwich> = S & {
inside: NPSelection,
};
export type ComplementSelection = {
type: "complement",
selection: AdjectiveSelection
| LocativeAdverbSelection
| SandwichSelection<Sandwich>
| NounSelection,
};
export type Sandwich = {
type: "sandwich",
before: PsString | undefined,
@ -898,6 +927,7 @@ export type VerbRenderedBlock = {
hasBa: boolean,
ps: SingleOrLengthOpts<PsString[]>,
person: Person,
complement: undefined | Rendered<ComplementSelection> | Rendered<UnselectedComplementSelection>,
},
};
@ -907,6 +937,8 @@ export type Block = {
| Rendered<ObjectSelectionComplete>
| Rendered<APSelection>
| Rendered<PredicateSelectionComplete>
| Rendered<ComplementSelection>
| Rendered<UnselectedComplementSelection>
| PerfectParticipleBlock
| PerfectEquativeBlock
| ModalVerbBlock

View File

@ -46,4 +46,6 @@ module.exports = [
1527815348, // تلل - to go
1527815216, // راتلل - to come
1527819674, // څملاستل - to lie down
]
1581086654898, // کېدل - to become
1527812754, // کېدل - to happen
];

View File

@ -87,4 +87,6 @@ module.exports = [
1527815214, // راوړل - to bring
1527819827, // راوستل - to bring
1527812275, // لیدل - to see
]
1579015359582, // کول - to make
1527812752, // کول - to do
];