added possesives! - still not quite there with participles
This commit is contained in:
parent
c469c0f2f3
commit
e2a90e6315
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@lingdocs/pashto-inflector",
|
||||
"version": "2.3.4",
|
||||
"version": "2.3.5",
|
||||
"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",
|
||||
|
|
|
@ -27,6 +27,7 @@ import { isAdjectiveEntry, isLocativeAdverbEntry, isNounEntry } from "./lib/type
|
|||
import defualtTextOptions from "./lib/default-text-options";
|
||||
import PhraseBuilder from "./components/vp-explorer/VPExplorer";
|
||||
import useStickyState from "./lib/useStickyState";
|
||||
import { EPExplorer } from "./library";
|
||||
type VerbType = "simple" | "stative compound" | "dynamic compound";
|
||||
const verbTypes: VerbType[] = [
|
||||
"simple",
|
||||
|
@ -276,6 +277,11 @@ function App() {
|
|||
opts={textOptions}
|
||||
/>
|
||||
</div>}
|
||||
<h4>🆕 Equative Phrase Builder</h4>
|
||||
<EPExplorer
|
||||
opts={textOptions}
|
||||
entryFeeder={entryFeeder}
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
<Modal show={showingTextOptions} onHide={() => setShowingTextOptions(false)}>
|
||||
|
|
|
@ -15,12 +15,14 @@ import { getForms } from "../lib/conjugation-forms";
|
|||
import { conjugateVerb } from "../lib/verb-conjugation";
|
||||
import PersonSelection from "./PersonSelection";
|
||||
import {
|
||||
personIsAllowed,
|
||||
randomPerson,
|
||||
incrementPerson,
|
||||
parseEc,
|
||||
} from "../lib/misc-helpers";
|
||||
import * as T from "../types";
|
||||
import {
|
||||
randomPerson,
|
||||
isInvalidSubjObjCombo,
|
||||
} from "../lib/np-tools";
|
||||
|
||||
const VerbChoiceWarning = () => (
|
||||
<>
|
||||
|
@ -100,7 +102,7 @@ function reducer(state: State, action: Action): State {
|
|||
let newPerson = person;
|
||||
let otherPerson = state[oppositeRole(setting)];
|
||||
let otherSetting = oppositeRole(setting);
|
||||
while (!personIsAllowed(newPerson, otherPerson)) {
|
||||
while (isInvalidSubjObjCombo(newPerson, otherPerson)) {
|
||||
otherPerson = incrementPerson(otherPerson);
|
||||
}
|
||||
return { ...state, [setting]: newPerson, [otherSetting]: otherPerson };
|
||||
|
@ -127,9 +129,9 @@ function reducer(state: State, action: Action): State {
|
|||
case "randomPerson":
|
||||
return {
|
||||
...state,
|
||||
[action.payload]: randomPerson(
|
||||
state[action.payload === "subject" ? "object" : "subject"]
|
||||
),
|
||||
[action.payload]: randomPerson({
|
||||
prev: state[action.payload === "subject" ? "object" : "subject"]
|
||||
}),
|
||||
};
|
||||
case "setShowingFormInfo":
|
||||
return {
|
||||
|
|
|
@ -31,7 +31,8 @@ function EPExplorer(props: {
|
|||
tense: "present",
|
||||
negative: false,
|
||||
},
|
||||
}, "EPSelectionState2");
|
||||
shrunkenPossesive: undefined,
|
||||
}, "EPSelectionState3");
|
||||
function handlePredicateTypeChange(type: "NP" | "Complement") {
|
||||
setEps(o => ({
|
||||
...o,
|
||||
|
@ -56,6 +57,12 @@ function EPExplorer(props: {
|
|||
},
|
||||
}));
|
||||
}
|
||||
function handleShrinkPossesive(shrunkenPossesive: number | undefined) {
|
||||
setEps(o => ({
|
||||
...o,
|
||||
shrunkenPossesive,
|
||||
}));
|
||||
}
|
||||
const king = eps.subject?.type === "pronoun"
|
||||
? "subject"
|
||||
: eps.predicate.type === "Complement"
|
||||
|
@ -76,6 +83,8 @@ function EPExplorer(props: {
|
|||
{mode === "phrases" && <>
|
||||
<div className="my-2">
|
||||
<NPPicker
|
||||
shrunkenPossesiveInPhrase={eps.shrunkenPossesive}
|
||||
handleShrinkPossesive={handleShrinkPossesive}
|
||||
heading={<div className="h5 text-center">Subject {king === "subject" ? roleIcon.king : ""}</div>}
|
||||
entryFeeder={props.entryFeeder}
|
||||
np={eps.subject}
|
||||
|
@ -99,6 +108,8 @@ function EPExplorer(props: {
|
|||
/>
|
||||
</div>
|
||||
{eps.predicate.type === "NP" ? <NPPicker
|
||||
shrunkenPossesiveInPhrase={eps.shrunkenPossesive}
|
||||
handleShrinkPossesive={handleShrinkPossesive}
|
||||
entryFeeder={props.entryFeeder}
|
||||
np={eps.predicate.type === "NP" ? eps.predicate.NP : undefined}
|
||||
counterPart={undefined}
|
||||
|
|
|
@ -36,14 +36,12 @@ function AdjectiveManager(props: {
|
|||
// flippedList.reverse();
|
||||
// console.log(props.adjectives);
|
||||
return <div className="mb-1">
|
||||
{!!props.adjectives.length && <div className="d-flex flex-row justify-content-between">
|
||||
<h6>Adjectives</h6>
|
||||
{!adding ? <h6 onClick={() => setAdding(true)}>+ Adj.</h6> : <div></div>}
|
||||
</div>}
|
||||
{adding && <div>
|
||||
<div className="d-flex flex-row justify-content-between">
|
||||
<div className="d-flex flex-row justify-content-between mb-1">
|
||||
<div>Add Adjective</div>
|
||||
<div onClick={() => setAdding(false)}>Cancel</div>
|
||||
<div className="clickable" onClick={() => setAdding(false)}>
|
||||
<i className="fas fa-trash" />
|
||||
</div>
|
||||
</div>
|
||||
<AdjectivePicker
|
||||
noTitle
|
||||
|
@ -53,21 +51,27 @@ function AdjectiveManager(props: {
|
|||
onChange={handleAddNew}
|
||||
/>
|
||||
</div>}
|
||||
{props.adjectives.map((adj, i) => (
|
||||
<div className="d-flex flex-row align-items-baseline">
|
||||
<AdjectivePicker
|
||||
noTitle
|
||||
key={`adj${i}`}
|
||||
adjective={adj}
|
||||
entryFeeder={props.entryFeeder}
|
||||
opts={props.opts}
|
||||
onChange={handleChange(i)}
|
||||
/>
|
||||
<div onClick={deleteAdj(i)} className="ml-4">
|
||||
<div className="fas fa-trash" />
|
||||
{props.adjectives.map((adj, i) => <div key={i}>
|
||||
<div className="d-flex flex-row justify-content-between">
|
||||
<div>Adjective</div>
|
||||
<div className="d-flex flex-row align-items-baseline">
|
||||
{!!props.adjectives.length && !adding && <div>
|
||||
<h6 onClick={() => setAdding(true)}>+ Adj.</h6>
|
||||
</div>}
|
||||
<div onClick={deleteAdj(i)} className="ml-4">
|
||||
<div className="fas fa-trash" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<AdjectivePicker
|
||||
noTitle
|
||||
key={`adj${i}`}
|
||||
adjective={adj}
|
||||
entryFeeder={props.entryFeeder}
|
||||
opts={props.opts}
|
||||
onChange={handleChange(i)}
|
||||
/>
|
||||
</div>)}
|
||||
{!adding && !props.adjectives.length && <h6 className="clickable" style={{ float: "right" }}>
|
||||
<div onClick={() => setAdding(true)}>+ Adj.</div>
|
||||
</h6>}
|
||||
|
|
|
@ -5,6 +5,7 @@ function makeParticipleSelection(verb: T.VerbEntry): T.ParticipleSelection {
|
|||
return {
|
||||
type: "participle",
|
||||
verb,
|
||||
possesor: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,10 @@ function NPPicker(props: {
|
|||
onChange: (nps: T.NPSelection | undefined) => void,
|
||||
np: T.NPSelection | undefined,
|
||||
counterPart: T.NPSelection | T.VerbObject | undefined,
|
||||
role: "subject" | "object" | "ergative",
|
||||
role: "subject" | "object" | "ergative" | "possesor",
|
||||
opts: T.TextOptions,
|
||||
handleShrinkPossesive: (uid: number | undefined) => void,
|
||||
shrunkenPossesiveInPhrase: number | undefined,
|
||||
cantClear?: boolean,
|
||||
is2ndPersonPicker?: boolean,
|
||||
entryFeeder: T.EntryFeeder,
|
||||
|
@ -27,6 +29,7 @@ function NPPicker(props: {
|
|||
if (props.is2ndPersonPicker && ((props.np?.type !== "pronoun") || !isSecondPerson(props.np.person))) {
|
||||
throw new Error("can't use 2ndPerson NPPicker without a pronoun");
|
||||
}
|
||||
const [addingPoss, setAddingPoss] = useState<boolean>(false);
|
||||
const [npType, setNpType] = useState<T.NPType | undefined>(props.np ? props.np.type : undefined);
|
||||
useEffect(() => {
|
||||
setNpType(props.np ? props.np.type : undefined);
|
||||
|
@ -51,10 +54,32 @@ function NPPicker(props: {
|
|||
setNpType(ntp);
|
||||
}
|
||||
}
|
||||
function handlePossesiveChange(p: T.NPSelection | undefined) {
|
||||
if (!props.np || props.np.type === "pronoun") return;
|
||||
if (!p) {
|
||||
props.onChange({
|
||||
...props.np,
|
||||
possesor: undefined,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const isNewPosesser = checkForNewPossesor(p, props.np.possesor);
|
||||
const possesor = {
|
||||
np: p,
|
||||
uid: (!isNewPosesser && props.np.possesor) ? props.np.possesor.uid : makeUID(),
|
||||
};
|
||||
props.onChange({
|
||||
...props.np,
|
||||
possesor,
|
||||
});
|
||||
}
|
||||
const isDynamicComplement = props.np && props.np.type === "noun" && props.np.dynamicComplement;
|
||||
const clearButton = (!props.cantClear && !props.is2ndPersonPicker && !isDynamicComplement)
|
||||
? <button className="btn btn-sm btn-light mb-2" onClick={handleClear}>X</button>
|
||||
: <div></div>;
|
||||
const possesiveUid = (props.np && props.np.type !== "pronoun" && props.np.possesor)
|
||||
? props.np.possesor.uid
|
||||
: undefined;
|
||||
return <>
|
||||
<div className="d-flex flex-row justify-content-between">
|
||||
<div></div>
|
||||
|
@ -68,21 +93,54 @@ function NPPicker(props: {
|
|||
</div>
|
||||
</div>
|
||||
<div style={{ minWidth: "9rem" }}>
|
||||
{!npType && <div className="text-center">
|
||||
<div className="h6 mr-3">
|
||||
Choose NP
|
||||
</div>
|
||||
{npTypes.map((npt) => <div key={npt} className="mb-2">
|
||||
<button
|
||||
key={npt}
|
||||
type="button"
|
||||
className="mr-2 btn btn-sm btn-outline-secondary"
|
||||
onClick={() => handleNPTypeChange(npt)}
|
||||
>
|
||||
{npt}
|
||||
</button>
|
||||
{!npType && <div className="text-center">
|
||||
<div className="h6 mr-3">
|
||||
Choose NP
|
||||
</div>
|
||||
{npTypes.map((npt) => <div key={npt} className="mb-2">
|
||||
<button
|
||||
key={npt}
|
||||
type="button"
|
||||
className="mr-2 btn btn-sm btn-outline-secondary"
|
||||
onClick={() => handleNPTypeChange(npt)}
|
||||
>
|
||||
{npt}
|
||||
</button>
|
||||
</div>)}
|
||||
</div>}
|
||||
{(props.np && props.np.type !== "pronoun" && (props.np.possesor || addingPoss)) && <div className="mb-3" style={{ paddingLeft: "0.5rem", borderLeft: "1px solid grey" }}>
|
||||
<div className="d-flex flex-row text-muted mb-2">
|
||||
<div>Possesive:</div>
|
||||
{props.np.possesor && <div className="clickable mx-2" onClick={() => {
|
||||
props.handleShrinkPossesive(possesiveUid === props.shrunkenPossesiveInPhrase
|
||||
? undefined
|
||||
: possesiveUid
|
||||
);
|
||||
}}>
|
||||
{possesiveUid === props.shrunkenPossesiveInPhrase ? "👶 Shrunken" : "Shrink"}
|
||||
</div>}
|
||||
<div className="clickable ml-2" onClick={() => {
|
||||
setAddingPoss(false);
|
||||
handlePossesiveChange(undefined);
|
||||
}}>
|
||||
<i className="fas fa-trash" />
|
||||
</div>
|
||||
</div>
|
||||
<NPPicker
|
||||
onChange={handlePossesiveChange}
|
||||
counterPart={undefined}
|
||||
cantClear
|
||||
np={props.np.possesor ? props.np.possesor.np : undefined}
|
||||
handleShrinkPossesive={props.handleShrinkPossesive}
|
||||
shrunkenPossesiveInPhrase={props.shrunkenPossesiveInPhrase}
|
||||
role="possesor"
|
||||
opts={props.opts}
|
||||
entryFeeder={props.entryFeeder}
|
||||
/>
|
||||
</div>}
|
||||
{(npType === "noun" || npType === "participle") && props.np && !addingPoss && <div>
|
||||
<span className="clickable text-muted" onClick={() => setAddingPoss(true)}>+ Possesive</span>
|
||||
</div>}
|
||||
{(npType === "pronoun" && props.np?.type === "pronoun")
|
||||
? <PronounPicker
|
||||
role={props.role}
|
||||
|
@ -111,6 +169,26 @@ function NPPicker(props: {
|
|||
</>;
|
||||
}
|
||||
|
||||
// {(npType && !isDynamicComplement) && }
|
||||
function checkForNewPossesor(n: T.NPSelection | undefined, old: T.PossesorSelection | undefined): boolean {
|
||||
if (!old || !n) {
|
||||
return true;
|
||||
}
|
||||
if (n.type !== old.np.type) {
|
||||
return true;
|
||||
}
|
||||
if (n.type === "pronoun") return false;
|
||||
if (n.type === "noun" && old.np.type === "noun") {
|
||||
return n.entry.ts !== old.np.entry.ts;
|
||||
}
|
||||
if (n.type === "participle" && old.np.type === "participle") {
|
||||
return n.verb.entry.ts !== old.np.verb.entry.ts;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: BETTER UID
|
||||
function makeUID() {
|
||||
return Math.floor(Math.random() * 50000);
|
||||
}
|
||||
|
||||
export default NPPicker;
|
|
@ -12,7 +12,7 @@ const gColors = {
|
|||
};
|
||||
|
||||
// TODO: better logic on this
|
||||
const labels = (role: "subject" | "object" | "ergative") => ({
|
||||
const labels = (role: "subject" | "object" | "ergative" | "possesor") => ({
|
||||
// persons: [
|
||||
// ["1st", "1st pl."],
|
||||
// ["2nd", "2nd pl."],
|
||||
|
@ -20,11 +20,15 @@ const labels = (role: "subject" | "object" | "ergative") => ({
|
|||
// ],
|
||||
e: role === "object" ? [
|
||||
["me", "us"],
|
||||
["you", "you pl."],
|
||||
["you", "y'all"],
|
||||
[{ masc: "him/it", fem: "her/it"}, "them"],
|
||||
] : role === "possesor" ? [
|
||||
["my", "our"],
|
||||
["your", "y'all's"],
|
||||
[{ masc: "his/its", fem: "her/its"}, "their"],
|
||||
] : [
|
||||
["I", "We"],
|
||||
["You", "You pl."],
|
||||
["You", "Y'all"],
|
||||
[{ masc: "He/It", fem: "She/It"}, "They"],
|
||||
],
|
||||
p: role === "subject" ? {
|
||||
|
@ -49,6 +53,17 @@ const labels = (role: "subject" | "object" | "ergative") => ({
|
|||
["ته", "تاسو"],
|
||||
[{ masc: "دهٔ", fem: "دې" }, "دوي"],
|
||||
],
|
||||
} : role === "possesor" ? {
|
||||
far: [
|
||||
["زما", "زمونږ"],
|
||||
["ستا", "ستاسو"],
|
||||
[{ masc: "د هغهٔ", fem: "د هغې" }, "د هغوي"],
|
||||
],
|
||||
near: [
|
||||
["زما", "زمونږ"],
|
||||
["ستا", "ستاسو"],
|
||||
[{ masc: "د دهٔ", fem: "د دې" }, "د دوي"],
|
||||
],
|
||||
} : {
|
||||
far: [
|
||||
["ما", "مونږ"],
|
||||
|
@ -79,11 +94,10 @@ function pickerStateToPerson(s: PickerState): T.Person {
|
|||
+ (6 * s.col);
|
||||
}
|
||||
|
||||
function NPPronounPicker({ onChange, pronoun, role, clearButton, opts, is2ndPersonPicker }: {
|
||||
function NPPronounPicker({ onChange, pronoun, role, opts, is2ndPersonPicker }: {
|
||||
pronoun: T.PronounSelection,
|
||||
onChange: (p: T.PronounSelection) => void,
|
||||
role: "object" | "subject" | "ergative",
|
||||
clearButton?: JSX.Element,
|
||||
role: "object" | "subject" | "ergative" | "possesor",
|
||||
opts: T.TextOptions,
|
||||
is2ndPersonPicker?: boolean,
|
||||
}) {
|
||||
|
|
|
@ -106,6 +106,7 @@ export function makeNounSelection(entry: T.NounEntry, dynamicComplement?: true):
|
|||
number,
|
||||
numberCanChange: number === "singular",
|
||||
adjectives: [],
|
||||
possesor: undefined,
|
||||
dynamicComplement,
|
||||
};
|
||||
}
|
|
@ -45,7 +45,7 @@ export function VPExplorer(props: {
|
|||
}) {
|
||||
const [vps, setVps] = useStickyState<T.VPSelectionState>(
|
||||
savedVps => makeVPSelectionState(props.verb, savedVps),
|
||||
"vpsState5",
|
||||
"vpsState6",
|
||||
);
|
||||
const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">(
|
||||
savedMode => {
|
||||
|
@ -97,6 +97,12 @@ export function VPExplorer(props: {
|
|||
if (vps.verb?.isCompound === "dynamic") return;
|
||||
setVps(switchSubjObj)
|
||||
}
|
||||
function handleShrinkPossesive(shrunkenPossesive: number | undefined) {
|
||||
setVps(o => ({
|
||||
...o,
|
||||
shrunkenPossesive,
|
||||
}));
|
||||
}
|
||||
function quizLock<T>(f: T) {
|
||||
if (mode === "quiz") {
|
||||
return () => {
|
||||
|
@ -142,10 +148,12 @@ export function VPExplorer(props: {
|
|||
? "ergative"
|
||||
: "subject"
|
||||
}
|
||||
shrunkenPossesiveInPhrase={vps.shrunkenPossesive}
|
||||
is2ndPersonPicker={vps.verb.tenseCategory === "imperative"}
|
||||
np={vps.subject}
|
||||
counterPart={vps.verb ? vps.verb.object : undefined}
|
||||
onChange={handleSubjectChange}
|
||||
handleShrinkPossesive={handleShrinkPossesive}
|
||||
opts={props.opts}
|
||||
/>
|
||||
</div>
|
||||
|
@ -153,6 +161,8 @@ export function VPExplorer(props: {
|
|||
{(typeof vps.verb.object === "number")
|
||||
? <div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div>
|
||||
: <NPPicker
|
||||
shrunkenPossesiveInPhrase={vps.shrunkenPossesive}
|
||||
handleShrinkPossesive={handleShrinkPossesive}
|
||||
heading={roles.king === "object"
|
||||
? <div className="h5 text-center clickable" onClick={() => setShowingExplanation({ role: "king", item: "object" })}>Object {roleIcon.king}</div>
|
||||
: <div className="h5 text-center clickable" onClick={() => setShowingExplanation({ role: "servant", item: "object" })}>Object {roleIcon.servant}</div>}
|
||||
|
|
|
@ -445,6 +445,7 @@ function getRandomVPSelection(mix: MixType = "both") {
|
|||
return {
|
||||
subject: subject !== undefined ? subject : randSubj,
|
||||
verb: randomizeTense(verb, true),
|
||||
shrunkenPossesive: undefined,
|
||||
}
|
||||
}
|
||||
const v: T.VerbSelectionComplete = {
|
||||
|
@ -460,6 +461,7 @@ function getRandomVPSelection(mix: MixType = "both") {
|
|||
return {
|
||||
subject: randSubj,
|
||||
verb: randomizeTense(v, true),
|
||||
shrunkenPossesive: undefined,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -95,5 +95,6 @@ export function makeVPSelectionState(
|
|||
},
|
||||
} : {},
|
||||
},
|
||||
shrunkenPossesive: os ? os.shrunkenPossesive : undefined,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -162,41 +162,8 @@ export function randFromArray<M>(arr: M[]): M {
|
|||
];
|
||||
}
|
||||
|
||||
// TODO: deprecate this because we have it in np-tools?
|
||||
/**
|
||||
* Sees if a possiblePerson (for subject/object) is possible, given the other person
|
||||
*
|
||||
* @param possiblePerson
|
||||
* @param existingPerson
|
||||
*/
|
||||
export function personIsAllowed(possiblePerson: T.Person, existingPerson?: T.Person): boolean {
|
||||
const isFirstPerson = (p: T.Person) => [0, 1, 6, 7].includes(p);
|
||||
const isSecondPerson = (p: T.Person) => [2, 3, 8, 9].includes(p);
|
||||
// can't have both subject and object be 1st person
|
||||
if (isFirstPerson(possiblePerson) && (existingPerson && isFirstPerson(existingPerson))) {
|
||||
return false;
|
||||
}
|
||||
// can't have both subject and object be 2nd person
|
||||
if (isSecondPerson(possiblePerson) && (existingPerson && isSecondPerson(existingPerson))) {
|
||||
return false;
|
||||
}
|
||||
// otherwise it's ok
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: deprecate this because we have it in np-tools?
|
||||
/**
|
||||
* Picks a random person while assuring that the other person is not in conflict
|
||||
*
|
||||
* @param other
|
||||
*/
|
||||
export function randomPerson(other?: T.Person): T.Person {
|
||||
let newPerson: T.Person;
|
||||
do {
|
||||
newPerson = randomNumber(0, 12);
|
||||
} while(!personIsAllowed(newPerson, other));
|
||||
return newPerson;
|
||||
}
|
||||
export const isFirstPerson = (p: T.Person) => [0, 1, 6, 7].includes(p);
|
||||
export const isSecondPerson = (p: T.Person) => [2, 3, 8, 9].includes(p);
|
||||
|
||||
export function incrementPerson(p: T.Person): T.Person {
|
||||
return (p + 1) % 12;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import * as T from "../types";
|
||||
import { parseEc } from "../lib/misc-helpers";
|
||||
import { isFirstPerson, parseEc } from "../lib/misc-helpers";
|
||||
import { isSecondPerson } from "./phrase-building/vp-tools";
|
||||
|
||||
function getRandPers(): T.Person {
|
||||
return Math.floor(Math.random() * 12);
|
||||
|
@ -31,22 +32,10 @@ export function randomPerson(a?: { prev?: T.Person, counterPart?: T.VerbObject |
|
|||
}
|
||||
|
||||
export function isInvalidSubjObjCombo(subj: T.Person, obj: T.Person): boolean {
|
||||
const firstPeople = [
|
||||
T.Person.FirstSingMale,
|
||||
T.Person.FirstSingFemale,
|
||||
T.Person.FirstPlurMale,
|
||||
T.Person.FirstPlurFemale,
|
||||
];
|
||||
const secondPeople = [
|
||||
T.Person.SecondSingMale,
|
||||
T.Person.SecondSingFemale,
|
||||
T.Person.SecondPlurMale,
|
||||
T.Person.SecondPlurFemale,
|
||||
];
|
||||
return (
|
||||
(firstPeople.includes(subj) && firstPeople.includes(obj))
|
||||
(isFirstPerson(subj) && isFirstPerson(obj))
|
||||
||
|
||||
(secondPeople.includes(subj) && secondPeople.includes(obj))
|
||||
(isSecondPerson(subj) && isSecondPerson(obj))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,11 @@ import {
|
|||
} from "./segment";
|
||||
import { removeAccents } from "../accent-helpers";
|
||||
import { getEnglishFromRendered, getPashtoFromRendered } from "./np-tools";
|
||||
import {
|
||||
orderKidsSection,
|
||||
findPossesiveToShrink,
|
||||
shrinkNP,
|
||||
} from "./compile-tools";
|
||||
|
||||
export function compileEP(EP: T.EPRendered, form: T.FormVersion): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string[] };
|
||||
export function compileEP(EP: T.EPRendered, form: T.FormVersion, combineLengths: true): { ps: T.PsString[], e?: string[] };
|
||||
|
@ -37,12 +42,20 @@ function getSegmentsAndKids(EP: T.EPRendered, form: T.FormVersion): { kids: Segm
|
|||
}
|
||||
return [s];
|
||||
}
|
||||
const subject = makeSegment(getPashtoFromRendered(EP.subject));
|
||||
const predicate = makeSegment(getPashtoFromRendered(EP.predicate));
|
||||
const possToShrink = findPossesiveToShrink(EP);
|
||||
const shrunkenPossAllowed = !(form.removeKing && possToShrink?.role === "king");
|
||||
const possUid = shrunkenPossAllowed ? EP.shrunkenPossesive : undefined;
|
||||
const subject = makeSegment(getPashtoFromRendered(EP.subject, possUid, false));
|
||||
const predicate = makeSegment(getPashtoFromRendered(EP.predicate, possUid, false));
|
||||
return {
|
||||
kids: EP.equative.hasBa
|
||||
? [makeSegment(grammarUnits.baParticle, ["isBa", "isKid"])]
|
||||
: [],
|
||||
kids: orderKidsSection([
|
||||
...EP.equative.hasBa
|
||||
? [makeSegment(grammarUnits.baParticle, ["isBa", "isKid"])]
|
||||
: [],
|
||||
...(possToShrink && shrunkenPossAllowed)
|
||||
? [shrinkNP(possToShrink)]
|
||||
: [],
|
||||
]),
|
||||
NPs: [
|
||||
...ifNotRemoved(subject, "subject"),
|
||||
...ifNotRemoved(predicate, "predicate"),
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
import * as T from "../../types";
|
||||
import {
|
||||
Segment,
|
||||
makeSegment,
|
||||
} from "./segment";
|
||||
import { getVerbBlockPosFromPerson } from "../misc-helpers";
|
||||
import { pronouns } from "../grammar-units";
|
||||
|
||||
export function orderKidsSection(kids: Segment[]): Segment[] {
|
||||
const sorted = [...kids];
|
||||
return sorted.sort((a, b) => {
|
||||
// ba first
|
||||
if (a.isBa) return -1;
|
||||
// kinds lined up 1st 2nd 3rd person
|
||||
if (a.isMiniPronoun && b.isMiniPronoun) {
|
||||
if (a.isMiniPronoun < b.isMiniPronoun) {
|
||||
return -1;
|
||||
}
|
||||
if (a.isMiniPronoun > b.isMiniPronoun) {
|
||||
return 1;
|
||||
}
|
||||
// TODO: is this enough?
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
export function findPossesiveToShrink(VP: T.VPRendered | T.EPRendered): T.Rendered<T.NPSelection> | undefined {
|
||||
const uid = VP.shrunkenPossesive;
|
||||
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.uid === uid) {
|
||||
return NP.possesor.np;
|
||||
}
|
||||
return findPossesiveInNP(NP.possesor.np);
|
||||
}
|
||||
if (uid === undefined) return undefined;
|
||||
const objPred: T.Rendered<T.NPSelection> | undefined = ("object" in VP)
|
||||
? (typeof VP.object === "object" ? VP.object : undefined)
|
||||
: (VP.predicate.type === "noun" || VP.predicate.type === "participle" || VP.predicate.type === "pronoun")
|
||||
// typescript is dumb here;
|
||||
? VP.predicate as T.Rendered<T.NPSelection>
|
||||
: undefined;
|
||||
return (
|
||||
findPossesiveInNP(VP.subject)
|
||||
||
|
||||
findPossesiveInNP(objPred)
|
||||
);
|
||||
}
|
||||
|
||||
export function shrinkNP(np: T.Rendered<T.NPSelection>): Segment {
|
||||
function getFirstSecThird(): 1 | 2 | 3 {
|
||||
if ([0, 1, 6, 7].includes(np.person)) return 1;
|
||||
if ([2, 3, 8, 9].includes(np.person)) return 2;
|
||||
return 3;
|
||||
}
|
||||
const [row, col] = getVerbBlockPosFromPerson(np.person);
|
||||
return makeSegment(pronouns.mini[row][col], ["isKid", getFirstSecThird()]);
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import * as T from "../../types";
|
||||
import {
|
||||
concatPsString,
|
||||
concatPsString, psStringEquals,
|
||||
} from "../p-text-helpers";
|
||||
import {
|
||||
Segment,
|
||||
|
@ -13,7 +13,6 @@ import {
|
|||
import {
|
||||
removeAccents,
|
||||
} from "../accent-helpers";
|
||||
import { getVerbBlockPosFromPerson } from "../misc-helpers";
|
||||
import * as grammarUnits from "../grammar-units";
|
||||
import {
|
||||
removeBa,
|
||||
|
@ -21,6 +20,11 @@ import {
|
|||
} from "./vp-tools";
|
||||
import { isImperativeTense, isModalTense, isPerfectTense } from "../type-predicates";
|
||||
import { getEnglishFromRendered, getPashtoFromRendered } from "./np-tools";
|
||||
import {
|
||||
orderKidsSection,
|
||||
findPossesiveToShrink,
|
||||
shrinkNP,
|
||||
} from "./compile-tools";
|
||||
|
||||
type Form = T.FormVersion & { OSV?: boolean };
|
||||
export function compileVP(VP: T.VPRendered, form: Form): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] };
|
||||
|
@ -79,20 +83,33 @@ function compilePs({ NPs, kids, verb: { head, rest }, VP }: CompilePsInput): T.S
|
|||
}
|
||||
|
||||
function getSegmentsAndKids(VP: T.VPRendered, form: Form): { kids: Segment[], NPs: Segment[][] } {
|
||||
const SO = {
|
||||
subject: getPashtoFromRendered(VP.subject),
|
||||
object: typeof VP.object === "object" ? getPashtoFromRendered(VP.object) : undefined,
|
||||
}
|
||||
const removeKing = form.removeKing && !(VP.isCompound === "dynamic" && VP.isPast);
|
||||
const shrinkServant = form.shrinkServant && !(VP.isCompound === "dynamic" && !VP.isPast);
|
||||
|
||||
const toShrink = (() => {
|
||||
const toShrinkServant = (() => {
|
||||
if (!shrinkServant) return undefined;
|
||||
if (!VP.servant) return undefined;
|
||||
const servant = VP[VP.servant];
|
||||
if (typeof servant !== "object") return undefined;
|
||||
return servant;
|
||||
})();
|
||||
const shrunkenServant = toShrinkServant ? shrinkNP(toShrinkServant) : undefined;
|
||||
const possToShrink = findPossesiveToShrink(VP);
|
||||
const shrunkenPossesive = possToShrink ? shrinkNP(possToShrink) : undefined;
|
||||
const shrunkenPossAllowed = possToShrink && shrunkenPossesive && (
|
||||
!shrunkenServant || !psStringEquals(shrunkenPossesive.ps[0], shrunkenServant.ps[0])
|
||||
) && (
|
||||
// can only shrink the possesive if the parent of the possesive is still in full form
|
||||
!(possToShrink.role === "king" && removeKing)
|
||||
&&
|
||||
!(possToShrink.role === "servant" && shrinkServant)
|
||||
);
|
||||
const shrinkPossUid = shrunkenPossAllowed
|
||||
? VP.shrunkenPossesive
|
||||
: undefined;
|
||||
const SO = {
|
||||
subject: getPashtoFromRendered(VP.subject, shrinkPossUid, false),
|
||||
object: typeof VP.object === "object" ? getPashtoFromRendered(VP.object, shrinkPossUid, VP.subject.person) : undefined,
|
||||
};
|
||||
function getSegment(t: "subject" | "object"): Segment | undefined {
|
||||
const word = (VP.servant === t)
|
||||
? (!shrinkServant ? SO[t] : undefined)
|
||||
|
@ -106,12 +123,14 @@ function getSegmentsAndKids(VP: T.VPRendered, form: Form): { kids: Segment[], NP
|
|||
const object = getSegment("object");
|
||||
|
||||
return {
|
||||
kids: [
|
||||
kids: orderKidsSection([
|
||||
...VP.verb.hasBa
|
||||
? [makeSegment(grammarUnits.baParticle, ["isBa", "isKid"])] : [],
|
||||
...toShrink
|
||||
? [shrinkNP(toShrink)] : [],
|
||||
],
|
||||
...shrunkenServant
|
||||
? [shrunkenServant] : [],
|
||||
...(shrunkenPossesive && shrunkenPossAllowed)
|
||||
? [shrunkenPossesive] : [],
|
||||
]),
|
||||
NPs: [
|
||||
[
|
||||
...subject ? [subject] : [],
|
||||
|
@ -236,11 +255,6 @@ function arrangeVerbWNegative(head: T.PsString | undefined, restRaw: T.PsString[
|
|||
];
|
||||
}
|
||||
|
||||
function shrinkNP(np: T.Rendered<T.NPSelection>): Segment {
|
||||
const [row, col] = getVerbBlockPosFromPerson(np.person);
|
||||
return makeSegment(grammarUnits.pronouns.mini[row][col], ["isKid", "isMiniPronoun"]);
|
||||
}
|
||||
|
||||
function mergeSegments(s1: Segment, s2: Segment, noSpace?: "no space"): Segment {
|
||||
if (noSpace) {
|
||||
return s2.adjust({ ps: (p) => concatPsString(s1.ps[0], p) });
|
||||
|
@ -295,4 +309,3 @@ function compileEnglish(VP: T.VPRendered): string[] | undefined {
|
|||
}))
|
||||
: undefined;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import {
|
||||
isFirstPerson,
|
||||
isSecondPerson,
|
||||
} from "../../lib/misc-helpers";
|
||||
import * as T from "../../types";
|
||||
import { concatPsString } from "../p-text-helpers";
|
||||
|
||||
export function getPashtoFromRendered(np: T.Rendered<T.NPSelection | T.EqCompSelection>): T.PsString[] {
|
||||
function getBaseAndAdjectives(np: T.Rendered<T.NPSelection | T.EqCompSelection>): T.PsString[] {
|
||||
const adjs = np.adjectives;
|
||||
if (!adjs) {
|
||||
return np.ps;
|
||||
|
@ -18,25 +22,106 @@ export function getPashtoFromRendered(np: T.Rendered<T.NPSelection | T.EqCompSel
|
|||
));
|
||||
}
|
||||
|
||||
export function getEnglishFromRendered(np: T.Rendered<T.NPSelection | T.EqCompSelection>): string | undefined {
|
||||
if (!np.e) return undefined;
|
||||
if (np.type === "noun") {
|
||||
try {
|
||||
// split out the atricles so adjectives can be stuck inbetween them and the word
|
||||
const chunks = np.e.split("the)");
|
||||
const [articles, word] = chunks.length === 1
|
||||
? ["", np.e]
|
||||
: [chunks[0] + "the) ", chunks[1]];
|
||||
const adjs = !np.adjectives
|
||||
? ""
|
||||
: np.adjectives.reduce((accum, curr): string => {
|
||||
if (!curr.e) throw new Error("no english for adjective");
|
||||
return accum + curr.e + " ";
|
||||
}, "");
|
||||
return `${articles}${adjs}${word}`;
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
export function getPashtoFromRendered(np: T.Rendered<T.NPSelection | T.EqCompSelection>, shrunkenPossesive: number | undefined, subjectsPerson: false | T.Person): T.PsString[] {
|
||||
const base = getBaseAndAdjectives(np);
|
||||
if (!np.possesor || np.possesor.uid === shrunkenPossesive) {
|
||||
return base;
|
||||
}
|
||||
return np.e;
|
||||
return addPossesor(np.possesor.np, base, subjectsPerson);
|
||||
}
|
||||
|
||||
|
||||
function addPossesor(owner: T.Rendered<T.NPSelection>, existing: T.PsString[], subjectsPerson: false | T.Person): T.PsString[] {
|
||||
function willBeReflexive(subj: T.Person, obj: T.Person): boolean {
|
||||
return (
|
||||
([0, 1].includes(subj) && [0, 1].includes(obj))
|
||||
||
|
||||
([2, 3].includes(subj) && [8, 9].includes(obj))
|
||||
);
|
||||
}
|
||||
const wPossesor = existing.flatMap(ps => (
|
||||
getBaseAndAdjectives(owner).map(v => (
|
||||
(owner.type === "pronoun" && subjectsPerson !== false && willBeReflexive(subjectsPerson, owner.person))
|
||||
? concatPsString({ p: "خپل", f: "khpul" }, " ", ps)
|
||||
: (owner.type === "pronoun" && isFirstPerson(owner.person))
|
||||
? concatPsString({ p: "ز", f: "z" }, v, " ", ps)
|
||||
: (owner.type === "pronoun" && isSecondPerson(owner.person))
|
||||
? concatPsString({ p: "س", f: "s" }, v, " ", ps)
|
||||
: concatPsString({ p: "د", f: "du" }, " ", v, " ", ps)
|
||||
))
|
||||
));
|
||||
if (!owner.possesor) {
|
||||
return wPossesor;
|
||||
}
|
||||
return addPossesor(owner.possesor.np, wPossesor, subjectsPerson);
|
||||
}
|
||||
|
||||
function addArticlesAndAdjs(np: T.Rendered<T.NounSelection>): string | undefined {
|
||||
if (!np.e) return undefined;
|
||||
try {
|
||||
// split out the atricles so adjectives can be stuck inbetween them and the word
|
||||
const chunks = np.e.split("the)");
|
||||
const [articles, word] = chunks.length === 1
|
||||
? ["", np.e]
|
||||
: [chunks[0] + "the) ", chunks[1]];
|
||||
const adjs = !np.adjectives
|
||||
? ""
|
||||
: np.adjectives.reduce((accum, curr): string => {
|
||||
if (!curr.e) throw new Error("no english for adjective");
|
||||
return accum + curr.e + " ";
|
||||
}, "");
|
||||
return `${articles}${adjs}${word}`;
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function addPossesors(possesor: T.Rendered<T.NPSelection> | undefined, base: string | undefined): string | undefined {
|
||||
function removeArticles(s: string): string {
|
||||
return s.replace("(the) ", "").replace("(a/the) ", "");
|
||||
}
|
||||
if (!base) return undefined;
|
||||
if (!possesor) return base;
|
||||
if (possesor.type === "pronoun") {
|
||||
return `${pronounPossEng(possesor.person)} ${removeArticles(base)}`;
|
||||
}
|
||||
const possesorE = getEnglishFromRendered(possesor);
|
||||
if (!possesorE) return undefined;
|
||||
return `${possesorE}'s ${removeArticles(base)}`;
|
||||
}
|
||||
|
||||
function pronounPossEng(p: T.Person): string {
|
||||
if (p === T.Person.FirstSingMale || p === T.Person.FirstSingFemale) {
|
||||
return "my";
|
||||
}
|
||||
if (p === T.Person.FirstPlurMale || p === T.Person.FirstPlurFemale) {
|
||||
return "our";
|
||||
}
|
||||
if (p === T.Person.SecondSingMale || p === T.Person.SecondSingFemale) {
|
||||
return "your";
|
||||
}
|
||||
if (p === T.Person.SecondPlurMale || p === T.Person.SecondPlurFemale) {
|
||||
return "your (pl.)";
|
||||
}
|
||||
if (p === T.Person.ThirdSingMale) {
|
||||
return "his/its";
|
||||
}
|
||||
if (p === T.Person.ThirdSingFemale) {
|
||||
return "her/its";
|
||||
}
|
||||
return "their";
|
||||
}
|
||||
|
||||
export function getEnglishFromRendered(r: T.Rendered<T.NPSelection | T.EqCompSelection>): string | undefined {
|
||||
if (!r.e) return undefined;
|
||||
if (r.type === "loc. adv." || r.type === "adjective") {
|
||||
return r.e;
|
||||
}
|
||||
if (r.type === "noun") {
|
||||
// TODO: shouldn't have to do this 'as' - should be automatically narrowing
|
||||
const np = r as T.Rendered<T.NounSelection>;
|
||||
return addPossesors(np.possesor?.np, addArticlesAndAdjs(np));
|
||||
}
|
||||
// TODO: possesives in English for participles and pronouns too!
|
||||
return r.e;
|
||||
}
|
|
@ -17,7 +17,7 @@ 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): T.Rendered<T.AdjectiveSelection> {
|
||||
export function renderAdjectiveSelection(a: T.AdjectiveSelection, person: T.Person, inflected: boolean, role: "king" | "servant" | "none"): T.Rendered<T.AdjectiveSelection> {
|
||||
const infs = inflectWord(a.entry);
|
||||
const eWord = getEnglishWord(a.entry);
|
||||
const e = !eWord
|
||||
|
@ -31,6 +31,7 @@ export function renderAdjectiveSelection(a: T.AdjectiveSelection, person: T.Pers
|
|||
ps: [psStringFromEntry(a.entry)],
|
||||
e,
|
||||
inflected: false,
|
||||
role,
|
||||
person,
|
||||
}
|
||||
if (!infs.inflections || !isUnisexSet(infs.inflections)) {
|
||||
|
@ -42,6 +43,7 @@ export function renderAdjectiveSelection(a: T.AdjectiveSelection, person: T.Pers
|
|||
ps: chooseInflection(infs.inflections, person, inflected),
|
||||
e,
|
||||
inflected: false,
|
||||
role,
|
||||
person,
|
||||
};
|
||||
}
|
|
@ -8,12 +8,17 @@ import { getPersonFromVerbForm } from "../../lib/misc-helpers";
|
|||
import { getVerbBlockPosFromPerson } from "../misc-helpers";
|
||||
import { getEnglishWord } from "../get-english-word";
|
||||
import { psStringFromEntry } from "../p-text-helpers";
|
||||
import { personGender, personIsPlural } from "../../library";
|
||||
import { isLocativeAdverbEntry } from "../type-predicates";
|
||||
import { renderAdjectiveSelection } from "./render-adj";
|
||||
|
||||
export function renderEP(EP: T.EPSelectionComplete): T.EPRendered {
|
||||
const kingPerson = (EP.subject.type === "pronoun")
|
||||
const king = (EP.subject.type === "pronoun")
|
||||
? "subject"
|
||||
: EP.predicate.type === "NP"
|
||||
? "predicate"
|
||||
: "subject";
|
||||
// TODO: less repetative logic
|
||||
const kingPerson = king === "subject"
|
||||
? getPersonFromNP(EP.subject)
|
||||
: EP.predicate.type === "NP"
|
||||
? getPersonFromNP(EP.predicate.selection)
|
||||
|
@ -21,12 +26,13 @@ export function renderEP(EP: T.EPSelectionComplete): T.EPRendered {
|
|||
return {
|
||||
type: "EPRendered",
|
||||
king: EP.predicate.type === "Complement" ? "subject" : "predicate",
|
||||
subject: renderNPSelection(EP.subject, false, false, "subject"),
|
||||
subject: renderNPSelection(EP.subject, false, false, "subject", king === "subject" ? "king" : "none"),
|
||||
predicate: EP.predicate.type === "NP"
|
||||
? renderNPSelection(EP.predicate.selection, false, true, "subject")
|
||||
? renderNPSelection(EP.predicate.selection, false, true, "subject", "king")
|
||||
: renderEqCompSelection(EP.predicate.selection, kingPerson),
|
||||
equative: renderEquative(EP.equative, kingPerson),
|
||||
englishBase: equativeBuilders[EP.equative.tense](kingPerson, EP.equative.negative),
|
||||
shrunkenPossesive: EP.shrunkenPossesive,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -68,10 +74,11 @@ function renderEqCompSelection(s: T.EqCompSelection, person: T.Person): T.Render
|
|||
e,
|
||||
inflected: false,
|
||||
person,
|
||||
role: "none",
|
||||
};
|
||||
}
|
||||
if (s.type === "adjective") {
|
||||
return renderAdjectiveSelection(s, person, false)
|
||||
return renderAdjectiveSelection(s, person, false, "none")
|
||||
}
|
||||
throw new Error("invalid EqCompSelection");
|
||||
}
|
||||
|
@ -136,9 +143,9 @@ function getEnglishConj(p: T.Person, e: string | T.EnglishBlock): string {
|
|||
return e[row][col];
|
||||
}
|
||||
|
||||
function chooseInflection(inflections: T.UnisexSet<T.InflectionSet>, pers: T.Person): T.ArrayOneOrMore<T.PsString> {
|
||||
const gender = personGender(pers);
|
||||
const plural = personIsPlural(pers);
|
||||
return inflections[gender][plural ? 1 : 0];
|
||||
}
|
||||
// function chooseInflection(inflections: T.UnisexSet<T.InflectionSet>, pers: T.Person): T.ArrayOneOrMore<T.PsString> {
|
||||
// const gender = personGender(pers);
|
||||
// const plural = personIsPlural(pers);
|
||||
// return inflections[gender][plural ? 1 : 0];
|
||||
// }
|
||||
|
||||
|
|
|
@ -12,10 +12,11 @@ import {
|
|||
import { parseEc } from "../misc-helpers";
|
||||
import { getEnglishWord } from "../get-english-word";
|
||||
import { renderAdjectiveSelection } from "./render-adj";
|
||||
import { isPattern5Entry, isUnisexNounEntry } from "../type-predicates";
|
||||
|
||||
export function renderNPSelection(NP: T.NPSelection, inflected: boolean, inflectEnglish: boolean, role: "subject"): T.Rendered<T.NPSelection>
|
||||
export function renderNPSelection(NP: T.NPSelection | T.ObjectNP, inflected: boolean, inflectEnglish: boolean, role: "object"): T.Rendered<T.NPSelection> | T.Person.ThirdPlurMale | "none";
|
||||
export function renderNPSelection(NP: T.NPSelection | T.ObjectNP, inflected: boolean, inflectEnglish: boolean, role: "subject" | "object"): T.Rendered<T.NPSelection> | T.Person.ThirdPlurMale | "none" {
|
||||
export function renderNPSelection(NP: T.NPSelection, inflected: boolean, inflectEnglish: boolean, role: "subject", soRole: "servant" | "king" | "none"): T.Rendered<T.NPSelection>;
|
||||
export function renderNPSelection(NP: T.NPSelection | T.ObjectNP, inflected: boolean, inflectEnglish: boolean, role: "object", soRole: "servant" | "king" | "none"): T.Rendered<T.NPSelection> | T.Person.ThirdPlurMale | "none";
|
||||
export function renderNPSelection(NP: T.NPSelection | T.ObjectNP, inflected: boolean, inflectEnglish: boolean, role: "subject" | "object", soRole: "servant" | "king" | "none"): T.Rendered<T.NPSelection> | T.Person.ThirdPlurMale | "none" {
|
||||
if (typeof NP !== "object") {
|
||||
if (role !== "object") {
|
||||
throw new Error("ObjectNP only allowed for objects");
|
||||
|
@ -23,18 +24,18 @@ export function renderNPSelection(NP: T.NPSelection | T.ObjectNP, inflected: boo
|
|||
return NP;
|
||||
}
|
||||
if (NP.type === "noun") {
|
||||
return renderNounSelection(NP, inflected);
|
||||
return renderNounSelection(NP, inflected, soRole);
|
||||
}
|
||||
if (NP.type === "pronoun") {
|
||||
return renderPronounSelection(NP, inflected, inflectEnglish);
|
||||
return renderPronounSelection(NP, inflected, inflectEnglish, soRole);
|
||||
}
|
||||
if (NP.type === "participle") {
|
||||
return renderParticipleSelection(NP, inflected)
|
||||
return renderParticipleSelection(NP, inflected, soRole)
|
||||
}
|
||||
throw new Error("unknown NP type");
|
||||
};
|
||||
|
||||
function renderNounSelection(n: T.NounSelection, inflected: boolean): T.Rendered<T.NounSelection> {
|
||||
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);
|
||||
|
@ -52,32 +53,51 @@ function renderNounSelection(n: T.NounSelection, inflected: boolean): T.Rendered
|
|||
const person = getPersonNumber(n.gender, n.number);
|
||||
return {
|
||||
...n,
|
||||
adjectives: n.adjectives.map(a => renderAdjectiveSelection(a, person, inflected)),
|
||||
adjectives: n.adjectives.map(a => renderAdjectiveSelection(a, person, inflected, role)),
|
||||
person,
|
||||
inflected,
|
||||
role,
|
||||
ps: pashto,
|
||||
e: english,
|
||||
possesor: renderPossesor(n.possesor, role),
|
||||
};
|
||||
}
|
||||
|
||||
function renderPronounSelection(p: T.PronounSelection, inflected: boolean, englishInflected: boolean): T.Rendered<T.PronounSelection> {
|
||||
function renderPronounSelection(p: T.PronounSelection, inflected: boolean, englishInflected: boolean, role: "servant" | "king" | "none"): T.Rendered<T.PronounSelection> {
|
||||
const [row, col] = getVerbBlockPosFromPerson(p.person);
|
||||
return {
|
||||
...p,
|
||||
inflected,
|
||||
role,
|
||||
ps: grammarUnits.pronouns[p.distance][inflected ? "inflected" : "plain"][row][col],
|
||||
e: grammarUnits.persons[p.person].label[englishInflected ? "object" : "subject"],
|
||||
};
|
||||
}
|
||||
|
||||
function renderParticipleSelection(p: T.ParticipleSelection, inflected: boolean): T.Rendered<T.ParticipleSelection> {
|
||||
function renderParticipleSelection(p: T.ParticipleSelection, inflected: boolean, role: "servant" | "king" | "none"): T.Rendered<T.ParticipleSelection> {
|
||||
return {
|
||||
...p,
|
||||
inflected,
|
||||
role,
|
||||
person: T.Person.ThirdPlurMale,
|
||||
// TODO: More robust inflection of inflecting pariticiples - get from the conjugation engine
|
||||
ps: [psStringFromEntry(p.verb.entry)].map(ps => inflected ? concatPsString(ps, { p: "و", f: "o" }) : ps),
|
||||
e: getEnglishParticiple(p.verb.entry),
|
||||
possesor: renderPossesor(p.possesor, role),
|
||||
};
|
||||
}
|
||||
|
||||
function renderPossesor(possesor: { np: T.NPSelection, uid: number } | undefined, possesorRole: "servant" | "king" | "none"): { np: T.Rendered<T.NPSelection>, uid: number } | undefined {
|
||||
if (!possesor) return undefined;
|
||||
return {
|
||||
uid: possesor.uid,
|
||||
np: renderNPSelection(
|
||||
possesor.np,
|
||||
!(possesor.np.type === "noun" && isUnisexNounEntry(possesor.np.entry) && isPattern5Entry(possesor.np.entry)),
|
||||
false,
|
||||
"subject",
|
||||
possesorRole,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -47,11 +47,12 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
|
|||
type: "VPRendered",
|
||||
king,
|
||||
servant,
|
||||
shrunkenPossesive: VP.shrunkenPossesive,
|
||||
isPast,
|
||||
isTransitive,
|
||||
isCompound: VP.verb.isCompound,
|
||||
subject: renderNPSelection(VP.subject, inflectSubject, false, "subject"),
|
||||
object: renderNPSelection(VP.verb.object, inflectObject, true, "object"),
|
||||
subject: renderNPSelection(VP.subject, inflectSubject, false, "subject", king === "subject" ? "king" : "servant"),
|
||||
object: renderNPSelection(VP.verb.object, inflectObject, true, "object", king === "object" ? "king" : "servant"),
|
||||
verb: renderVerbSelection(VP.verb, kingPerson, objectPerson),
|
||||
englishBase: renderEnglishVPBase({
|
||||
subjectPerson,
|
||||
|
|
|
@ -12,7 +12,7 @@ type SegmentDescriptions = {
|
|||
isVerbHead?: boolean,
|
||||
isOoOrWaaHead?: boolean,
|
||||
isVerbRest?: boolean,
|
||||
isMiniPronoun?: boolean,
|
||||
isMiniPronoun?: number,
|
||||
isKid?: boolean,
|
||||
// TODO: Simplify to just isKidAfterHead?
|
||||
isKidBetweenHeadAndRest?: boolean,
|
||||
|
@ -25,28 +25,23 @@ export type Segment = { ps: T.PsString[] } & SegmentDescriptions & {
|
|||
adjust: (o: { ps?: T.PsString | T.PsString[] | ((ps: T.PsString) => T.PsString), desc?: SDT[] }) => Segment,
|
||||
};
|
||||
|
||||
function addAdjectives(ps: T.PsString[], adjectives?: T.Rendered<T.AdjectiveSelection>[]): T.PsString[] {
|
||||
if (!adjectives) return ps;
|
||||
return ps.map(p => {
|
||||
// TODO: more thorough use of all possible variends?
|
||||
return concatPsString(...adjectives.map(a => a.ps[0]), p);
|
||||
});
|
||||
}
|
||||
|
||||
export function makeSegment(
|
||||
input: T.Rendered<T.NounSelection> | T.PsString | T.PsString[],
|
||||
options?: (keyof SegmentDescriptions)[],
|
||||
input: T.PsString | T.PsString[],
|
||||
options?: (keyof SegmentDescriptions | 1 | 2 | 3)[],
|
||||
): Segment {
|
||||
const ps: T.PsString[] = Array.isArray(input)
|
||||
? input
|
||||
: "type" in input
|
||||
? addAdjectives(input.ps, input.adjectives)
|
||||
: [input];
|
||||
return {
|
||||
ps: Array.isArray(ps) ? ps : [ps],
|
||||
...options && options.reduce((all, curr) => ({
|
||||
...all,
|
||||
[curr]: true,
|
||||
...typeof curr === "number" ? {
|
||||
isMiniPronoun: curr,
|
||||
} : {
|
||||
[curr]: true,
|
||||
},
|
||||
}), {}),
|
||||
adjust: function(o): Segment {
|
||||
return {
|
||||
|
|
19
src/types.ts
19
src/types.ts
|
@ -501,6 +501,7 @@ export type VPRendered = {
|
|||
type: "VPRendered",
|
||||
king: "subject" | "object",
|
||||
servant: "subject" | "object" | undefined,
|
||||
shrunkenPossesive: number | undefined,
|
||||
isPast: boolean,
|
||||
isTransitive: boolean,
|
||||
isCompound: "stative" | "dynamic" | false,
|
||||
|
@ -529,11 +530,13 @@ export type Tense = EquativeTense | VerbTense | PerfectTense | ModalTense | Impe
|
|||
export type VPSelectionState = {
|
||||
subject: NPSelection | undefined,
|
||||
verb: VerbSelection,
|
||||
shrunkenPossesive: undefined | number,
|
||||
};
|
||||
|
||||
export type VPSelectionComplete = {
|
||||
subject: NPSelection,
|
||||
verb: VerbSelectionComplete,
|
||||
shrunkenPossesive: undefined | number,
|
||||
};
|
||||
|
||||
export type VerbSelectionComplete = Omit<VerbSelection, "object" | "verbTense" | "perfectTense" | "imperfectiveTense" | "tenseCategory"> & {
|
||||
|
@ -587,6 +590,11 @@ export type NPType = "noun" | "pronoun" | "participle";
|
|||
|
||||
export type ObjectNP = "none" | Person.ThirdPlurMale;
|
||||
|
||||
export type PossesorSelection = {
|
||||
uid: number,
|
||||
np: NPSelection,
|
||||
}
|
||||
|
||||
// TODO require/import Person and PsString
|
||||
export type NounSelection = {
|
||||
type: "noun",
|
||||
|
@ -597,6 +605,7 @@ export type NounSelection = {
|
|||
numberCanChange: boolean,
|
||||
dynamicComplement?: boolean,
|
||||
adjectives: AdjectiveSelection[],
|
||||
possesor: undefined | PossesorSelection,
|
||||
};
|
||||
|
||||
export type AdjectiveSelection = {
|
||||
|
@ -619,6 +628,7 @@ export type PronounSelection = {
|
|||
export type ParticipleSelection = {
|
||||
type: "participle",
|
||||
verb: VerbEntry,
|
||||
possesor: undefined | PossesorSelection,
|
||||
};
|
||||
|
||||
// not object
|
||||
|
@ -629,7 +639,7 @@ export type ReplaceKey<T, K extends string, R> = T extends Record<K, unknown> ?
|
|||
export type FormVersion = { removeKing: boolean, shrinkServant: boolean };
|
||||
|
||||
export type Rendered<T extends NPSelection | EqCompSelection | AdjectiveSelection> = ReplaceKey<
|
||||
Omit<T, "changeGender" | "changeNumber" | "changeDistance" | "adjectives">,
|
||||
Omit<T, "changeGender" | "changeNumber" | "changeDistance" | "adjectives" | "possesor">,
|
||||
"e",
|
||||
string
|
||||
> & {
|
||||
|
@ -637,8 +647,13 @@ export type Rendered<T extends NPSelection | EqCompSelection | AdjectiveSelectio
|
|||
e?: string,
|
||||
inflected: boolean,
|
||||
person: Person,
|
||||
role: "king" | "servant" | "none",
|
||||
// TODO: better recursive thing
|
||||
adjectives?: Rendered<AdjectiveSelection>[],
|
||||
possesor?: {
|
||||
uid: number,
|
||||
np: Rendered<NPSelection>,
|
||||
},
|
||||
};
|
||||
// TODO: recursive changing this down into the possesor etc.
|
||||
|
||||
|
@ -650,6 +665,7 @@ export type EPSelectionState = {
|
|||
Complement: EqCompSelection | undefined,
|
||||
},
|
||||
equative: EquativeSelection,
|
||||
shrunkenPossesive: undefined | number,
|
||||
};
|
||||
|
||||
export type EPSelectionComplete = Omit<EPSelectionState, "subject" | "predicate"> & {
|
||||
|
@ -684,6 +700,7 @@ export type EPRendered = {
|
|||
predicate: Rendered<NPSelection | EqCompSelection>,
|
||||
equative: EquativeRendered,
|
||||
englishBase?: string[],
|
||||
shrunkenPossesive: undefined | number,
|
||||
}
|
||||
|
||||
export type EntryFeeder = {
|
||||
|
|
Loading…
Reference in New Issue