rough sandwich working
This commit is contained in:
parent
92b80c3b6a
commit
e8ec806ecb
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@lingdocs/pashto-inflector",
|
||||
"version": "2.5.5",
|
||||
"version": "2.5.6",
|
||||
"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",
|
||||
|
@ -76,7 +76,10 @@
|
|||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
],
|
||||
"rules": {
|
||||
"no-warning-comments": [1, {"terms": ["fixme", "xxx"], "location": "anywhere"}]
|
||||
}
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
|
|
@ -111,4 +111,69 @@ function EntrySelect<E extends T.DictionaryEntry | T.VerbEntry>(props: {
|
|||
</div>
|
||||
}
|
||||
|
||||
export function SandwichSelect<E extends T.Sandwich>(props: {
|
||||
sandwiches: E[],
|
||||
value: E | undefined,
|
||||
onChange: (value: E | undefined) => void,
|
||||
name: string | undefined,
|
||||
opts: T.TextOptions,
|
||||
style?: StyleHTMLAttributes<HTMLDivElement>,
|
||||
}) {
|
||||
const divStyle = props.style || { width: "13rem" };
|
||||
const placeholder = "Select Sandwich…";
|
||||
const value = props.value ? makeSandwichOption(props.value) : undefined;
|
||||
const options = props.sandwiches
|
||||
// .sort((a, b) => {
|
||||
// if ("entry" in a) {
|
||||
// return a.before.p.localeCompare("p" in b ? b.p : b.entry.p, "af-PS")
|
||||
// }
|
||||
// return a.p.localeCompare("p" in b ? b.p : b.entry.p, "af-PS");
|
||||
// })
|
||||
.map(makeSandwichOption);
|
||||
const onChange = (v: { label: string | JSX.Element, value: string } | null) => {
|
||||
if (!v) {
|
||||
props.onChange(undefined);
|
||||
return;
|
||||
}
|
||||
const s = props.sandwiches.find(e => {
|
||||
const sValue = JSON.parse(v.value) as T.Sandwich;
|
||||
if (sValue.type !== "sandwich") throw new Error("error converting selected sandwich value to a sandwich");
|
||||
return sandwichSideEq(e.before, sValue.before)
|
||||
&& sandwichSideEq(e.after, sValue.after)
|
||||
&& (e.e === sValue.e);
|
||||
});
|
||||
if (!s) return;
|
||||
props.onChange(s);
|
||||
}
|
||||
return <div style={divStyle}>
|
||||
<Select
|
||||
styles={customStyles}
|
||||
isSearchable={true}
|
||||
value={value}
|
||||
// @ts-ignore - gets messed up when using customStyles
|
||||
onChange={onChange}
|
||||
className="mb-2"
|
||||
options={options}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
function sandwichSideEq(s1: T.PsString | undefined, s2: T.PsString | undefined): boolean {
|
||||
if (s1 === undefined && s2 === undefined) {
|
||||
return true
|
||||
}
|
||||
if (typeof s1 === "object" && typeof s2 === "object" && s1.p === s2.p) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function makeSandwichOption(s: T.Sandwich): { label: string, value: string } {
|
||||
return {
|
||||
label: `${s.before ? s.before.p : ""} ... ${s.after ? s.after.p : ""} (${s.e})`,
|
||||
value: JSON.stringify(s),
|
||||
};
|
||||
}
|
||||
|
||||
export default EntrySelect;
|
|
@ -2,7 +2,8 @@ import { useState, useEffect } from "react";
|
|||
import * as T from "../../../types";
|
||||
import AdjectivePicker from "../../np-picker/AdjectivePicker";
|
||||
import LocativeAdverbPicker from "./LocativeAdverbPicker";
|
||||
const compTypes: T.EqCompType[] = ["adjective", "loc. adv."];
|
||||
import SandwichPicker from "../../np-picker/SandwichPicker";
|
||||
const compTypes: T.EqCompType[] = ["adjective", "loc. adv.", "sandwich"];
|
||||
|
||||
function EqCompPicker(props: {
|
||||
onChange: (comp: T.EqCompSelection | undefined) => void,
|
||||
|
@ -12,9 +13,13 @@ function EqCompPicker(props: {
|
|||
heading?: JSX.Element | string,
|
||||
entryFeeder: T.EntryFeeder,
|
||||
}) {
|
||||
const [compType, setCompType] = useState<T.EqCompType | undefined>(props.comp ? props.comp.type : undefined);
|
||||
const [compType, setCompType] = useState<T.EqCompType | undefined>(props.comp
|
||||
? props.comp.type
|
||||
: undefined);
|
||||
useEffect(() => {
|
||||
setCompType(props.comp ? props.comp.type : undefined);
|
||||
setCompType(props.comp
|
||||
? props.comp.type
|
||||
: undefined);
|
||||
}, [props.comp]);
|
||||
function handleClear() {
|
||||
setCompType(undefined);
|
||||
|
@ -69,6 +74,15 @@ function EqCompPicker(props: {
|
|||
opts={props.opts}
|
||||
onChange={props.onChange}
|
||||
/>
|
||||
: compType === "sandwich"
|
||||
? <SandwichPicker
|
||||
onChange={props.onChange}
|
||||
opts={props.opts}
|
||||
sandwich={props.comp?.type === "sandwich" ? props.comp : undefined}
|
||||
entryFeeder={props.entryFeeder}
|
||||
// TODO: get phraseIsComplete working here
|
||||
phraseIsComplete={false}
|
||||
/>
|
||||
: null}
|
||||
</div>
|
||||
</>;
|
||||
|
|
|
@ -137,7 +137,7 @@ function NPPronounPicker({ onChange, pronoun, role, opts, is2ndPersonPicker }: {
|
|||
const pSpec = is2ndPersonPicker
|
||||
? [pSpecA[1]]
|
||||
: pSpecA;
|
||||
return <div style={{ maxWidth: "145px", padding: 0 }}>
|
||||
return <div style={{ maxWidth: "145px", padding: 0, margin: "0 auto" }}>
|
||||
<div className="d-flex flex-row justify-content-between mb-2">
|
||||
<ButtonSelect
|
||||
xSmall
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
import * as T from "../../types";
|
||||
import { sandwiches } from "../../lib/sandwiches";
|
||||
import { SandwichSelect } from "../../components/EntrySelect";
|
||||
import { useState } from "react";
|
||||
import NPPicker from "./NPPicker";
|
||||
|
||||
function SandwichPicker(props: {
|
||||
opts: T.TextOptions,
|
||||
onChange: (s: T.SandwichSelection<T.Sandwich> | undefined) => void,
|
||||
sandwich: T.SandwichSelection<T.Sandwich> | undefined;
|
||||
entryFeeder: T.EntryFeeder,
|
||||
phraseIsComplete: boolean,
|
||||
}) {
|
||||
const [sandwichBase, setSandwichBase] = useState<T.Sandwich | undefined>(undefined);
|
||||
function handleNounChange(n: T.NPSelection | undefined) {
|
||||
if (!n) {
|
||||
props.onChange(undefined);
|
||||
return;
|
||||
}
|
||||
if (!sandwichBase) return;
|
||||
props.onChange({
|
||||
...sandwichBase,
|
||||
inside: n,
|
||||
});
|
||||
}
|
||||
function handleSandwichChange(s: T.Sandwich | undefined) {
|
||||
if (!s) {
|
||||
setSandwichBase(undefined);
|
||||
props.onChange(undefined);
|
||||
return;
|
||||
}
|
||||
setSandwichBase(s);
|
||||
if (!props.sandwich) return;
|
||||
props.onChange({
|
||||
...props.sandwich,
|
||||
...s,
|
||||
});
|
||||
}
|
||||
return <div>
|
||||
{sandwichBase && <div className="mb-2" style={{ margin: "0 auto" }}>
|
||||
<NPPicker
|
||||
onChange={handleNounChange}
|
||||
np={props.sandwich ? props.sandwich.inside : undefined}
|
||||
counterPart={undefined}
|
||||
opts={props.opts}
|
||||
role="object"
|
||||
cantClear={true}
|
||||
entryFeeder={props.entryFeeder}
|
||||
phraseIsComplete={props.phraseIsComplete}
|
||||
/>
|
||||
</div>}
|
||||
<SandwichSelect
|
||||
name="sandwich"
|
||||
opts={props.opts}
|
||||
sandwiches={sandwiches}
|
||||
value={sandwichBase}
|
||||
onChange={handleSandwichChange}
|
||||
/>
|
||||
</div>;
|
||||
}
|
||||
|
||||
export default SandwichPicker;
|
|
@ -13,7 +13,7 @@ function VPDisplay({ VP, opts, setForm }: {
|
|||
if (!("type" in VP)) {
|
||||
return <div className="lead text-muted text-center mt-4">
|
||||
{(() => {
|
||||
const twoNPs = (VP.subject === undefined) && (VP.verb.object === undefined);
|
||||
const twoNPs = (VP.subject === undefined) && (VP.object === undefined);
|
||||
return `Choose NP${twoNPs ? "s " : ""} to make a phrase`;
|
||||
})()}
|
||||
</div>;
|
||||
|
|
|
@ -67,10 +67,9 @@ function VPExplorer(props: {
|
|||
}, 1500);
|
||||
}
|
||||
useEffect(() => {
|
||||
const newVps = makeVPSelectionState(props.verb, vps);
|
||||
adjustVps({
|
||||
type: "load vps",
|
||||
payload: newVps,
|
||||
type: "set verb",
|
||||
payload: props.verb,
|
||||
});
|
||||
// eslint-disable-next-line
|
||||
}, [props.verb]);
|
||||
|
@ -161,7 +160,7 @@ function VPExplorer(props: {
|
|||
{mode === "phrases" ? <i className="fas fa-share-alt" /> : ""}
|
||||
</div>
|
||||
</div>
|
||||
{(vps.verb && (typeof vps.verb.object === "object") && (vps.verb.isCompound !== "dynamic") && (vps.verb.tenseCategory !== "imperative") &&(mode === "phrases")) &&
|
||||
{(vps.verb && (typeof vps.object === "object") && (vps.verb.isCompound !== "dynamic") && (vps.verb.tenseCategory !== "imperative") &&(mode === "phrases")) &&
|
||||
<div className="text-center my-2">
|
||||
<button onClick={handleSubjObjSwap} className="btn btn-sm btn-light">
|
||||
<i className="fas fa-exchange-alt mr-2" /> subj/obj
|
||||
|
@ -192,14 +191,14 @@ function VPExplorer(props: {
|
|||
}
|
||||
is2ndPersonPicker={vps.verb.tenseCategory === "imperative"}
|
||||
np={vps.subject}
|
||||
counterPart={vps.verb ? vps.verb.object : undefined}
|
||||
counterPart={vps.verb ? vps.object : undefined}
|
||||
onChange={handleSubjectChange}
|
||||
opts={props.opts}
|
||||
isShrunk={(servantIsShrunk && roles.servant === "subject")}
|
||||
/>
|
||||
</div>
|
||||
{vps.verb && (vps.verb.object !== "none") && <div className="my-2" style={{ background: (servantIsShrunk && roles.servant === "object") ? shrunkenBackground : "inherit" }}>
|
||||
{(typeof vps.verb.object === "number")
|
||||
{vps.verb && (vps.object !== "none") && <div className="my-2" style={{ background: (servantIsShrunk && roles.servant === "object") ? shrunkenBackground : "inherit" }}>
|
||||
{(typeof vps.object === "number")
|
||||
? <div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div>
|
||||
: <NPPicker
|
||||
phraseIsComplete={phraseIsComplete}
|
||||
|
@ -218,7 +217,7 @@ function VPExplorer(props: {
|
|||
</div>}
|
||||
entryFeeder={props.entryFeeder}
|
||||
role="object"
|
||||
np={vps.verb.object}
|
||||
np={vps.object}
|
||||
counterPart={vps.subject}
|
||||
onChange={handleObjectChange}
|
||||
opts={props.opts}
|
||||
|
|
|
@ -375,8 +375,8 @@ function completeVPs(vps: T.VPSelectionState): T.VPSelectionComplete {
|
|||
const oldSubj = vps.subject?.type === "pronoun"
|
||||
? vps.subject.person
|
||||
: undefined;
|
||||
const oldObj = (typeof vps.verb.object === "object" && vps.verb.object.type === "pronoun")
|
||||
? vps.verb.object.person
|
||||
const oldObj = (typeof vps.object === "object" && vps.object.type === "pronoun")
|
||||
? vps.object.person
|
||||
: undefined;
|
||||
const { subj, obj } = randomSubjObj(
|
||||
oldSubj === undefined
|
||||
|
@ -389,17 +389,6 @@ function completeVPs(vps: T.VPSelectionState): T.VPSelectionComplete {
|
|||
const t = getTenseFromVerbSelection(vps.verb);
|
||||
const verb: T.VerbSelectionComplete = {
|
||||
...vps.verb,
|
||||
object: (
|
||||
(typeof vps.verb.object === "object" && !(vps.verb.object.type === "noun" && vps.verb.object.dynamicComplement))
|
||||
||
|
||||
vps.verb.object === undefined
|
||||
)
|
||||
? {
|
||||
type: "pronoun",
|
||||
distance: "far",
|
||||
person: obj,
|
||||
}
|
||||
: vps.verb.object,
|
||||
tense: isImperativeTense(t) ? "presentVerb" : t,
|
||||
};
|
||||
return {
|
||||
|
@ -409,18 +398,29 @@ function completeVPs(vps: T.VPSelectionState): T.VPSelectionComplete {
|
|||
distance: "far",
|
||||
person: subj,
|
||||
},
|
||||
object: (
|
||||
(typeof vps.object === "object" && !(vps.object.type === "noun" && vps.object.dynamicComplement))
|
||||
||
|
||||
vps.object === undefined
|
||||
)
|
||||
? {
|
||||
type: "pronoun",
|
||||
distance: "far",
|
||||
person: obj,
|
||||
}
|
||||
: vps.object,
|
||||
verb,
|
||||
};
|
||||
}
|
||||
|
||||
function getRandomVPSelection(mix: MixType = "both") {
|
||||
// TODO: Type safety to make sure it's safe?
|
||||
return ({ subject, verb }: T.VPSelectionComplete): T.VPSelectionComplete => {
|
||||
return ({ subject, verb, object }: T.VPSelectionComplete): T.VPSelectionComplete => {
|
||||
const oldSubj = (subject.type === "pronoun")
|
||||
? subject.person
|
||||
: undefined;
|
||||
const oldObj = (typeof verb.object === "object" && verb.object.type === "pronoun")
|
||||
? verb.object.person
|
||||
const oldObj = (typeof object === "object" && object.type === "pronoun")
|
||||
? object.person
|
||||
: undefined;
|
||||
const { subj, obj } = randomSubjObj(
|
||||
oldSubj !== undefined ? { subj: oldSubj, obj: oldObj } : undefined
|
||||
|
@ -433,8 +433,8 @@ function getRandomVPSelection(mix: MixType = "both") {
|
|||
distance: "far",
|
||||
person: subj,
|
||||
};
|
||||
const randObj: T.PronounSelection = typeof verb?.object === "object" && verb.object.type === "pronoun" ? {
|
||||
...verb.object,
|
||||
const randObj: T.PronounSelection = typeof object === "object" && object.type === "pronoun" ? {
|
||||
...object,
|
||||
person: obj,
|
||||
} : {
|
||||
type: "pronoun",
|
||||
|
@ -445,23 +445,21 @@ function getRandomVPSelection(mix: MixType = "both") {
|
|||
if (mix === "tenses") {
|
||||
return {
|
||||
subject: subject !== undefined ? subject : randSubj,
|
||||
object: object !== undefined ? object : randObj,
|
||||
verb: randomizeTense(verb, true),
|
||||
form: { removeKing: false, shrinkServant: false },
|
||||
}
|
||||
}
|
||||
const v: T.VerbSelectionComplete = {
|
||||
...verb,
|
||||
object: (
|
||||
(typeof verb.object === "object" && !(verb.object.type === "noun" && verb.object.dynamicComplement))
|
||||
||
|
||||
verb.object === undefined
|
||||
)
|
||||
? randObj
|
||||
: verb.object,
|
||||
};
|
||||
return {
|
||||
subject: randSubj,
|
||||
verb: randomizeTense(v, true),
|
||||
object: (
|
||||
(typeof object === "object" && !(object.type === "noun" && object.dynamicComplement))
|
||||
||
|
||||
object === undefined
|
||||
)
|
||||
? randObj
|
||||
: object,
|
||||
verb: randomizeTense(verb, true),
|
||||
form: { removeKing: false, shrinkServant: false },
|
||||
};
|
||||
};
|
||||
|
|
|
@ -15,12 +15,12 @@ export function makeVPSelectionState(
|
|||
function getTransObjFromos() {
|
||||
if (
|
||||
!os ||
|
||||
os.verb.object === "none" ||
|
||||
typeof os.verb.object === "number" ||
|
||||
os.object === "none" ||
|
||||
typeof os.object === "number" ||
|
||||
os.verb.isCompound === "dynamic" ||
|
||||
(os.verb.object?.type === "noun" && os.verb.object.dynamicComplement)
|
||||
(os.object?.type === "noun" && os.object.dynamicComplement)
|
||||
) return undefined;
|
||||
return os.verb.object;
|
||||
return os.object;
|
||||
}
|
||||
const transitivity: T.Transitivity = "grammaticallyTransitive" in info
|
||||
? "transitive"
|
||||
|
@ -47,6 +47,7 @@ export function makeVPSelectionState(
|
|||
: undefined;
|
||||
return {
|
||||
subject,
|
||||
object,
|
||||
verb: {
|
||||
type: "verb",
|
||||
verb: verb,
|
||||
|
@ -55,7 +56,6 @@ export function makeVPSelectionState(
|
|||
perfectTense: os ? os.verb.perfectTense : "presentPerfect",
|
||||
imperativeTense: os ? os.verb.imperativeTense : "imperfectiveImperative",
|
||||
tenseCategory: os ? os.verb.tenseCategory : "basic",
|
||||
object,
|
||||
transitivity,
|
||||
isCompound,
|
||||
voice: transitivity === "transitive"
|
||||
|
@ -70,27 +70,33 @@ export function makeVPSelectionState(
|
|||
};
|
||||
}
|
||||
|
||||
export function changeStatDyn(v: T.VerbSelection, s: "dynamic" | "stative"): T.VerbSelection {
|
||||
const info = getVerbInfo(v.verb.entry, v.verb.complement);
|
||||
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)) {
|
||||
return v;
|
||||
}
|
||||
return {
|
||||
...v,
|
||||
isCompound: s,
|
||||
object: s === "dynamic"
|
||||
? makeNounSelection(info.dynamic.objComplement.entry as T.NounEntry, undefined, true)
|
||||
: undefined,
|
||||
dynAuxVerb: s === "dynamic"
|
||||
? { entry: info.dynamic.auxVerb } as T.VerbEntry
|
||||
: undefined,
|
||||
verb: {
|
||||
...v.verb,
|
||||
isCompound: s,
|
||||
dynAuxVerb: s === "dynamic"
|
||||
? { entry: info.dynamic.auxVerb } as T.VerbEntry
|
||||
: undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function changeTransitivity(v: T.VerbSelection, transitivity: "transitive" | "grammatically transitive"): T.VerbSelection {
|
||||
export function changeTransitivity(v: T.VPSelectionState, transitivity: "transitive" | "grammatically transitive"): T.VPSelectionState {
|
||||
return {
|
||||
...v,
|
||||
transitivity,
|
||||
object: transitivity === "grammatically transitive" ? T.Person.ThirdPlurMale : undefined,
|
||||
verb: {
|
||||
...v.verb,
|
||||
transitivity,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -10,6 +10,9 @@ import {
|
|||
isImperativeTense,
|
||||
} from "../../lib/type-predicates";
|
||||
import { checkForMiniPronounsError } from "../../lib/phrase-building/compile";
|
||||
import {
|
||||
makeVPSelectionState,
|
||||
} from "./verb-selection";
|
||||
|
||||
export type VpsReducerAction = {
|
||||
type: "load vps",
|
||||
|
@ -48,7 +51,10 @@ export type VpsReducerAction = {
|
|||
payload: "basic" | "modal" | "perfect" | "imperative",
|
||||
} | {
|
||||
type: "toggle servant shrink",
|
||||
};
|
||||
} | {
|
||||
type: "set verb",
|
||||
payload: T.VerbEntry,
|
||||
}
|
||||
|
||||
export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, sendAlert?: (msg: string) => void): T.VPSelectionState {
|
||||
return ensureMiniPronounsOk(vps, doReduce());
|
||||
|
@ -70,7 +76,7 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
|
|||
if (
|
||||
!skipPronounConflictCheck
|
||||
&&
|
||||
hasPronounConflict(subject, vps.verb?.object)
|
||||
hasPronounConflict(subject, vps.object)
|
||||
) {
|
||||
if (sendAlert) sendAlert("That combination of pronouns is not allowed");
|
||||
return vps;
|
||||
|
@ -82,7 +88,7 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
|
|||
}
|
||||
if (action.type === "set object") {
|
||||
if (!vps.verb) return vps;
|
||||
if ((vps.verb.object === "none") || (typeof vps.verb.object === "number")) {
|
||||
if ((vps.object === "none") || (typeof vps.object === "number")) {
|
||||
return vps;
|
||||
}
|
||||
const object = action.payload;
|
||||
|
@ -93,10 +99,7 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
|
|||
}
|
||||
return {
|
||||
...vps,
|
||||
verb: {
|
||||
...vps.verb,
|
||||
object,
|
||||
},
|
||||
object,
|
||||
};
|
||||
}
|
||||
if (action.type === "swap subj/obj") {
|
||||
|
@ -118,10 +121,10 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
|
|||
if (voice === "passive") {
|
||||
return {
|
||||
...vps,
|
||||
subject: typeof vps.verb.object === "object" ? vps.verb.object : undefined,
|
||||
subject: typeof vps.object === "object" ? vps.object : undefined,
|
||||
object: "none",
|
||||
verb: {
|
||||
...vps.verb,
|
||||
object: "none",
|
||||
voice,
|
||||
},
|
||||
};
|
||||
|
@ -129,10 +132,9 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
|
|||
return {
|
||||
...vps,
|
||||
subject: undefined,
|
||||
object: typeof vps.subject === "object" ? vps.subject : undefined,
|
||||
verb: {
|
||||
...vps.verb,
|
||||
// TODO: is this ok??
|
||||
object: typeof vps.subject === "object" ? vps.subject : undefined,
|
||||
voice,
|
||||
},
|
||||
};
|
||||
|
@ -143,17 +145,11 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
|
|||
}
|
||||
if (action.type === "set transitivity") {
|
||||
if (!(vps.verb && vps.verb.canChangeTransitivity)) return vps;
|
||||
return {
|
||||
...vps,
|
||||
verb: changeTransitivity(vps.verb, action.payload),
|
||||
};
|
||||
return changeTransitivity(vps, action.payload);
|
||||
}
|
||||
if (action.type === "set statDyn") {
|
||||
if (!(vps.verb && vps.verb.canChangeStatDyn)) return vps;
|
||||
return {
|
||||
...vps,
|
||||
verb: changeStatDyn(vps.verb, action.payload),
|
||||
};
|
||||
return changeStatDyn(vps, action.payload);
|
||||
}
|
||||
if (action.type === "set negativity") {
|
||||
if (!vps.verb) return vps;
|
||||
|
@ -220,14 +216,17 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
|
|||
},
|
||||
};
|
||||
}
|
||||
// if (action.type === "toggle servant shrink") {
|
||||
return {
|
||||
...vps,
|
||||
form: {
|
||||
...vps.form,
|
||||
shrinkServant: !vps.form.shrinkServant,
|
||||
},
|
||||
};
|
||||
if (action.type === "toggle servant shrink") {
|
||||
return {
|
||||
...vps,
|
||||
form: {
|
||||
...vps.form,
|
||||
shrinkServant: !vps.form.shrinkServant,
|
||||
},
|
||||
};
|
||||
}
|
||||
// if (action.type === "set verb") {
|
||||
return makeVPSelectionState(action.payload, vps);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,9 @@ import * as T from "../../types";
|
|||
import { concatPsString } from "../p-text-helpers";
|
||||
|
||||
function getBaseAndAdjectives(np: T.Rendered<T.NPSelection | T.EqCompSelection>): T.PsString[] {
|
||||
if (np.type === "sandwich") {
|
||||
return getSandwichPsBaseAndAdjectives(np);
|
||||
}
|
||||
const adjs = np.adjectives;
|
||||
if (!adjs) {
|
||||
return np.ps;
|
||||
|
@ -22,6 +25,32 @@ function getBaseAndAdjectives(np: T.Rendered<T.NPSelection | T.EqCompSelection>)
|
|||
));
|
||||
}
|
||||
|
||||
function getSandwichPsBaseAndAdjectives(s: T.Rendered<T.SandwichSelection<T.Sandwich>>): T.PsString[] {
|
||||
const insideBase = getBaseAndAdjectives(s.inside);
|
||||
const willContractWithPronoun = s.before && s.before.p === "د" && s.inside.type === "pronoun"
|
||||
&& (isFirstPerson(s.inside.person) || isSecondPerson(s.inside.person))
|
||||
const contracted = (willContractWithPronoun && s.inside.type === "pronoun")
|
||||
? contractPronoun(s.inside)
|
||||
: undefined
|
||||
return insideBase.map((inside) => (
|
||||
concatPsString(
|
||||
(s.before && !willContractWithPronoun) ? s.before : "",
|
||||
s.before ? " " : "",
|
||||
contracted ? contracted : inside,
|
||||
s.after ? " " : "",
|
||||
s.after ? s.after : "",
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
function contractPronoun(n: T.Rendered<T.PronounSelection>): T.PsString | undefined {
|
||||
return isFirstPerson(n.person)
|
||||
? concatPsString({ p: "ز", f: "z" }, n.ps[0])
|
||||
: isSecondPerson(n.person)
|
||||
? concatPsString({ p: "س", f: "s" }, n.ps[0])
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function trimOffShrunkenPossesive(p: T.Rendered<T.NPSelection>): T.Rendered<T.NPSelection> {
|
||||
if (!("possesor" in p)) {
|
||||
return p;
|
||||
|
@ -46,12 +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") {
|
||||
// ts being dumb
|
||||
const trimmed = trimOffShrunkenPossesive(np as T.Rendered<T.NPSelection>);
|
||||
if (trimmed.possesor) {
|
||||
return addPossesor(trimmed.possesor.np, base, subjectsPerson);
|
||||
}
|
||||
if (np.type === "loc. adv." || np.type === "adjective") {
|
||||
return base;
|
||||
}
|
||||
const trimmed = np.type === "sandwich" ? {
|
||||
...np,
|
||||
inside: trimOffShrunkenPossesive(np.inside),
|
||||
} : trimOffShrunkenPossesive(np);
|
||||
if (trimmed.type === "sandwich") {
|
||||
return trimmed.inside.possesor
|
||||
? addPossesor(trimmed.inside.possesor.np, base, subjectsPerson)
|
||||
: base;
|
||||
}
|
||||
if (trimmed.possesor) {
|
||||
return addPossesor(trimmed.possesor.np, base, subjectsPerson);
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
@ -143,6 +180,9 @@ function pronounPossEng(p: T.Person): string {
|
|||
}
|
||||
|
||||
export function getEnglishFromRendered(r: T.Rendered<T.NPSelection | T.EqCompSelection>): string | undefined {
|
||||
if (r.type === "sandwich") {
|
||||
return getEnglishFromRenderedSandwich(r);
|
||||
}
|
||||
if (!r.e) return undefined;
|
||||
if (r.type === "loc. adv." || r.type === "adjective") {
|
||||
return r.e;
|
||||
|
@ -155,3 +195,9 @@ export function getEnglishFromRendered(r: T.Rendered<T.NPSelection | T.EqCompSel
|
|||
// TODO: possesives in English for participles and pronouns too!
|
||||
return r.e;
|
||||
}
|
||||
|
||||
function getEnglishFromRenderedSandwich(r: T.Rendered<T.SandwichSelection<T.Sandwich>>): string | undefined {
|
||||
const insideE = getEnglishFromRendered(r.inside);
|
||||
if (!insideE) return undefined;
|
||||
return `${r.e} ${insideE}`;
|
||||
}
|
|
@ -10,6 +10,7 @@ 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";
|
||||
|
||||
export function renderEP(EP: T.EPSelectionComplete): T.EPRendered {
|
||||
const king = (EP.subject.type === "pronoun")
|
||||
|
@ -65,6 +66,9 @@ function renderEquative(es: T.EquativeSelection, person: T.Person): T.EquativeRe
|
|||
}
|
||||
|
||||
function renderEqCompSelection(s: T.EqCompSelection, person: T.Person): T.Rendered<T.EqCompSelection> {
|
||||
if (s.type === "sandwich") {
|
||||
return renderSandwich(s);
|
||||
}
|
||||
const e = getEnglishWord(s.entry);
|
||||
if (!e || typeof e !== "string") {
|
||||
console.log(e);
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
} from "../../lib/np-tools";
|
||||
import { getEnglishWord } from "../get-english-word";
|
||||
import { renderAdjectiveSelection } from "./render-adj";
|
||||
import { isPattern5Entry, isUnisexAnimNounEntry } from "../type-predicates";
|
||||
import { isPattern5Entry, isAnimNounEntry } from "../type-predicates";
|
||||
|
||||
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";
|
||||
|
@ -93,7 +93,7 @@ function renderPossesor(possesor: T.PossesorSelection | undefined, possesorRole:
|
|||
if (!possesor) return undefined;
|
||||
const isSingUnisexAnim5PatternNoun = (possesor.np.type === "noun"
|
||||
&& possesor.np.number === "singular"
|
||||
&& isUnisexAnimNounEntry(possesor.np.entry)
|
||||
&& isAnimNounEntry(possesor.np.entry)
|
||||
&& isPattern5Entry(possesor.np.entry)
|
||||
);
|
||||
return {
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import * as T from "../../types";
|
||||
import { isPattern1Entry, isPattern5Entry, isAnimNounEntry } from "../type-predicates";
|
||||
import { renderNPSelection } from "./render-np";
|
||||
|
||||
export function renderSandwich(s: T.SandwichSelection<T.Sandwich>): T.Rendered<T.SandwichSelection<T.Sandwich>> {
|
||||
const inflectInside = (isLocationSandwich(s) && s.inside.type === "noun" && isPattern1Entry(s.inside.entry) && s.inside.number === "singular")
|
||||
? false
|
||||
: (s.inside.type === "noun" && isPattern5Entry(s.inside.entry) && isAnimNounEntry(s.inside.entry))
|
||||
? false
|
||||
: true;
|
||||
return {
|
||||
...s,
|
||||
inside: renderNPSelection(
|
||||
s.inside,
|
||||
true,
|
||||
inflectInside,
|
||||
"subject",
|
||||
"none",
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
function isLocationSandwich(s: T.SandwichSelection<T.Sandwich>): boolean {
|
||||
// TODO: more nuanced version of this?
|
||||
return s.before?.p === "په" && s.after?.f === "کې";
|
||||
}
|
|
@ -28,20 +28,20 @@ import { renderNPSelection } from "./render-np";
|
|||
export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
|
||||
// Sentence Rules Logic
|
||||
const isPast = isPastTense(VP.verb.tense);
|
||||
const isTransitive = VP.verb.object !== "none";
|
||||
const isTransitive = VP.object !== "none";
|
||||
const { king, servant } = getKingAndServant(isPast, isTransitive);
|
||||
const kingPerson = getPersonFromNP(
|
||||
king === "subject" ? VP.subject : VP.verb.object,
|
||||
king === "subject" ? VP.subject : VP.object,
|
||||
);
|
||||
// TODO: more elegant way of handling this type safety
|
||||
if (kingPerson === undefined) {
|
||||
throw new Error("king of sentance does not exist");
|
||||
}
|
||||
const subjectPerson = getPersonFromNP(VP.subject);
|
||||
const objectPerson = getPersonFromNP(VP.verb.object);
|
||||
const objectPerson = getPersonFromNP(VP.object);
|
||||
// TODO: also don't inflect if it's a pattern one animate noun
|
||||
const inflectSubject = isPast && isTransitive && !isMascSingAnimatePattern4(VP.subject);
|
||||
const inflectObject = !isPast && isFirstOrSecondPersPronoun(VP.verb.object);
|
||||
const inflectObject = !isPast && isFirstOrSecondPersPronoun(VP.object);
|
||||
// Render Elements
|
||||
const b: T.VPRendered = {
|
||||
type: "VPRendered",
|
||||
|
@ -51,11 +51,11 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
|
|||
isTransitive,
|
||||
isCompound: VP.verb.isCompound,
|
||||
subject: renderNPSelection(VP.subject, inflectSubject, false, "subject", king === "subject" ? "king" : "servant"),
|
||||
object: renderNPSelection(VP.verb.object, inflectObject, true, "object", king === "object" ? "king" : "servant"),
|
||||
object: renderNPSelection(VP.object, inflectObject, true, "object", king === "object" ? "king" : "servant"),
|
||||
verb: renderVerbSelection(VP.verb, kingPerson, objectPerson),
|
||||
englishBase: renderEnglishVPBase({
|
||||
subjectPerson,
|
||||
object: VP.verb.isCompound === "dynamic" ? "none" : VP.verb.object,
|
||||
object: VP.verb.isCompound === "dynamic" ? "none" : VP.object,
|
||||
vs: VP.verb,
|
||||
}),
|
||||
form: VP.form,
|
||||
|
|
|
@ -197,48 +197,85 @@ export function switchSubjObj(vps: T.VPSelectionState): T.VPSelectionState;
|
|||
export function switchSubjObj(vps: T.VPSelectionComplete): T.VPSelectionComplete;
|
||||
export function switchSubjObj(vps: T.VPSelectionState | T.VPSelectionComplete): T.VPSelectionState | T.VPSelectionComplete {
|
||||
if ("tenseCategory" in vps.verb) {
|
||||
if (!vps.subject || !(typeof vps.verb.object === "object") || (vps.verb.tenseCategory === "imperative")) {
|
||||
if (!vps.subject || !(typeof vps.object === "object") || (vps.verb.tenseCategory === "imperative")) {
|
||||
return vps;
|
||||
}
|
||||
return {
|
||||
...vps,
|
||||
subject: vps.verb.object,
|
||||
verb: {
|
||||
...vps.verb,
|
||||
object: vps.subject,
|
||||
},
|
||||
subject: vps.object,
|
||||
object: vps.subject,
|
||||
};
|
||||
}
|
||||
if (!vps.subject|| !vps.verb || !(typeof vps.verb.object === "object")) {
|
||||
if (!vps.subject|| !vps.verb || !(typeof vps.object === "object")) {
|
||||
return vps;
|
||||
}
|
||||
return {
|
||||
...vps,
|
||||
subject: vps.verb.object,
|
||||
verb: {
|
||||
...vps.verb,
|
||||
object: vps.subject,
|
||||
}
|
||||
subject: vps.object,
|
||||
object: vps.subject,
|
||||
};
|
||||
}
|
||||
|
||||
// export function insertSubjectSelection(vps: T.VPSelectionState, s: T.SubjectSelection): T.VPSelectionState {
|
||||
// const index = vps.blocks.findIndex(f => f.type === "subjectSelection");
|
||||
// if (index === -1) {
|
||||
// throw new Error("couldn't find subjectSelection to insert over");
|
||||
// }
|
||||
// const blocks = [...vps.blocks];
|
||||
// blocks[index] = s;
|
||||
// return {
|
||||
// ...vps,
|
||||
// blocks,
|
||||
// };
|
||||
// }
|
||||
|
||||
// export function insertObjecttSelection(vps: T.VPSelectionState, o: T.ObjectSelection): T.VPSelectionState {
|
||||
// const index = vps.blocks.findIndex(f => f.type === "objectSelection");
|
||||
// if (index === -1) {
|
||||
// throw new Error("couldn't find objectSelection to insert over");
|
||||
// }
|
||||
// const blocks = [...vps.blocks];
|
||||
// blocks[index] = o;
|
||||
// return {
|
||||
// ...vps,
|
||||
// blocks,
|
||||
// };
|
||||
// }
|
||||
|
||||
// export function getSubjectSelection(vps: T.VPSelectionState): T.SubjectSelection {
|
||||
// const subject = vps.blocks.find(f => f.type === "subjectSelection");
|
||||
// if (subject?.type !== "subjectSelection") {
|
||||
// throw new Error("couldn't find subjectSelection");
|
||||
// }
|
||||
// return subject;
|
||||
// }
|
||||
|
||||
// export function getObjectSelection(vps: T.VPSelectionState): T.ObjectSelection {
|
||||
// const object = vps.blocks.find(f => f.type === "objectSelection");
|
||||
// if (object?.type !== "objectSelection") {
|
||||
// throw new Error("couldn't find objectSelection");
|
||||
// }
|
||||
// return object;
|
||||
// }
|
||||
|
||||
export function completeVPSelection(vps: T.VPSelectionState): T.VPSelectionComplete | undefined {
|
||||
if (vps.subject === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (vps.verb.object === undefined) {
|
||||
if (vps.object === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
// necessary for this version on typscript ...
|
||||
const verb: T.VerbSelectionComplete = {
|
||||
...vps.verb,
|
||||
object: vps.verb.object,
|
||||
tense: getTenseFromVerbSelection(vps.verb),
|
||||
};
|
||||
const subject = vps.subject;
|
||||
const object = vps.object
|
||||
return {
|
||||
...vps,
|
||||
subject,
|
||||
object,
|
||||
verb,
|
||||
};
|
||||
}
|
||||
|
@ -264,9 +301,9 @@ export function isThirdPerson(p: T.Person): boolean {
|
|||
export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState): T.VPSelectionState {
|
||||
console.log("checking more...", vps);
|
||||
const subjIs2ndPerson = (vps.subject?.type === "pronoun") && isSecondPerson(vps.subject.person);
|
||||
const objIs2ndPerson = (typeof vps.verb.object === "object")
|
||||
&& (vps.verb.object.type === "pronoun")
|
||||
&& isSecondPerson(vps.verb.object.person);
|
||||
const objIs2ndPerson = (typeof vps.object === "object")
|
||||
&& (vps.object.type === "pronoun")
|
||||
&& isSecondPerson(vps.object.person);
|
||||
console.log({ subjIs2ndPerson, objIs2ndPerson });
|
||||
const default2ndPersSubject: T.PronounSelection = {
|
||||
type: "pronoun",
|
||||
|
@ -284,22 +321,19 @@ export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState):
|
|||
return vps;
|
||||
}
|
||||
if (subjIs2ndPerson && objIs2ndPerson) {
|
||||
if (typeof vps.verb.object !== "object" || vps.verb.object.type !== "pronoun") {
|
||||
if (typeof vps.object !== "object" || vps.object.type !== "pronoun") {
|
||||
return vps;
|
||||
}
|
||||
return {
|
||||
...vps,
|
||||
verb: {
|
||||
...vps.verb,
|
||||
object: {
|
||||
...vps.verb.object,
|
||||
person: getNon2ndPersPronoun(),
|
||||
},
|
||||
object: {
|
||||
...vps.object,
|
||||
person: getNon2ndPersPronoun(),
|
||||
},
|
||||
};
|
||||
}
|
||||
if (!subjIs2ndPerson && objIs2ndPerson) {
|
||||
if (typeof vps.verb.object !== "object" || vps.verb.object.type !== "pronoun") {
|
||||
if (typeof vps.object !== "object" || vps.object.type !== "pronoun") {
|
||||
return {
|
||||
...vps,
|
||||
subject: default2ndPersSubject,
|
||||
|
@ -308,12 +342,9 @@ export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState):
|
|||
return {
|
||||
...vps,
|
||||
subject: default2ndPersSubject,
|
||||
verb: {
|
||||
...vps.verb,
|
||||
object: {
|
||||
...vps.verb.object,
|
||||
person: getNon2ndPersPronoun(),
|
||||
},
|
||||
object: {
|
||||
...vps.object,
|
||||
person: getNon2ndPersPronoun(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
import * as T from "../types";
|
||||
|
||||
export const sandwiches: T.Sandwich[] = [
|
||||
{
|
||||
type: "sandwich",
|
||||
before: { p: "له", f: "la" },
|
||||
after: { p: "نه", f: "na" },
|
||||
e: "from",
|
||||
},
|
||||
{
|
||||
type: "sandwich",
|
||||
before: { p: "له", f: "la" },
|
||||
after: { p: "څخه", f: "tsuxa" },
|
||||
e: "from",
|
||||
},
|
||||
// TODO: Implement mayonaise
|
||||
// {
|
||||
// type: "sandwich",
|
||||
// before: { p: "له", f: "la" },
|
||||
// after: "mayonaise",
|
||||
// e: "from",
|
||||
// },
|
||||
{
|
||||
type: "sandwich",
|
||||
before: { p: "له", f: "la" },
|
||||
after: { p: "سره", f: "sara" },
|
||||
e: "with",
|
||||
},
|
||||
{
|
||||
type: "sandwich",
|
||||
before: undefined,
|
||||
after: { p: "ته", f: "ta" },
|
||||
e: "to",
|
||||
},
|
||||
{
|
||||
type: "sandwich",
|
||||
before: { p: "د", f: "du" },
|
||||
after: { p: "په څانګ", f: "pu tsaang" },
|
||||
e: "beside",
|
||||
},
|
||||
{
|
||||
type: "sandwich",
|
||||
before: { p: "پر", f: "pur" },
|
||||
after: { p: "باندې", f: "baande" },
|
||||
e: "on",
|
||||
},
|
||||
{
|
||||
type: "sandwich",
|
||||
before: { p: "په", f: "pu" },
|
||||
after: { p: "کې", f: "ke" },
|
||||
e: "in",
|
||||
},
|
||||
{
|
||||
type: "sandwich",
|
||||
before: { p: "د", f: "du" },
|
||||
after: { p: "دننه", f: "dununa" },
|
||||
e: "inside",
|
||||
},
|
||||
{
|
||||
type: "sandwich",
|
||||
before: { p: "د", f: "du" },
|
||||
after: { p: "دباندې", f: "dubaande" },
|
||||
e: "outside",
|
||||
},
|
||||
{
|
||||
type: "sandwich",
|
||||
before: { p: "د", f: "du" },
|
||||
after: { p: "مخې ته", f: "mukhe ta" },
|
||||
e: "in front of",
|
||||
},
|
||||
{
|
||||
type: "sandwich",
|
||||
before: { p: "د", f: "du" },
|
||||
after: { p: "شا ته", f: "shaa ta" },
|
||||
e: "behind",
|
||||
},
|
||||
{
|
||||
type: "sandwich",
|
||||
before: { p: "د", f: "du" },
|
||||
after: { p: "لاندې", f: "laande" },
|
||||
e: "under",
|
||||
},
|
||||
{
|
||||
type: "sandwich",
|
||||
before: { p: "د", f: "du" },
|
||||
after: { p: "په شان", f: "pu shaan" },
|
||||
e: "like",
|
||||
},
|
||||
{
|
||||
type: "sandwich",
|
||||
before: { p: "د", f: "du" },
|
||||
after: { p: "غوندې", f: "ghwunde" },
|
||||
e: "like",
|
||||
},
|
||||
];
|
||||
|
||||
export default sandwiches;
|
|
@ -49,8 +49,12 @@ export function isUnisexNounEntry(e: T.NounEntry | T.AdjectiveEntry): e is T.Uni
|
|||
return isNounEntry(e) && e.c.includes("unisex");
|
||||
}
|
||||
|
||||
export function isAnimNounEntry(e: T.NounEntry | T.AdverbEntry): e is T.AnimNounEntry {
|
||||
return e.c.includes("anim.");
|
||||
}
|
||||
|
||||
export function isUnisexAnimNounEntry(e: T.NounEntry | T.AdjectiveEntry): e is T.UnisexAnimNounEntry {
|
||||
return isUnisexNounEntry(e) && e.c.includes("anim.");
|
||||
return isUnisexNounEntry(e) && isAnimNounEntry(e);
|
||||
}
|
||||
|
||||
export function isAdjOrUnisexNounEntry(e: T.Entry): e is (T.AdjectiveEntry | T.UnisexNounEntry) {
|
||||
|
@ -191,4 +195,3 @@ export function isEquativeTense(t: T.Tense): t is T.EquativeTense {
|
|||
export function isImperativeTense(tense: T.Tense): tense is T.ImperativeTense {
|
||||
return tense === "imperfectiveImperative" || tense === "perfectiveImperative";
|
||||
}
|
||||
|
||||
|
|
87
src/types.ts
87
src/types.ts
|
@ -472,6 +472,7 @@ export type AayTail = "ey" | "aay";
|
|||
export type NounEntry = DictionaryEntry & { c: string } & { __brand: "a noun entry" };
|
||||
export type MascNounEntry = NounEntry & { __brand2: "a masc noun entry" };
|
||||
export type FemNounEntry = NounEntry & { __brand2: "a fem noun entry" };
|
||||
export type AnimNounEntry = NounEntry & { __brand3: "a anim noun entry" };
|
||||
export type UnisexNounEntry = MascNounEntry & { __brand3: "a unisex noun entry" };
|
||||
export type UnisexAnimNounEntry = UnisexNounEntry & { __brand4: "an anim unisex noun entry" };
|
||||
export type AdverbEntry = DictionaryEntry & { c: string } & { __brand: "an adverb entry" };
|
||||
|
@ -529,20 +530,43 @@ export type ModalTense = `${VerbTense}Modal`;
|
|||
export type ImperativeTense = `${Aspect}Imperative`;
|
||||
export type Tense = EquativeTense | VerbTense | PerfectTense | ModalTense | ImperativeTense;
|
||||
|
||||
export type SubjectSelection = {
|
||||
type: "subjectSelection",
|
||||
selection: NPSelection | undefined,
|
||||
};
|
||||
|
||||
export type ObjectSelection = {
|
||||
type: "objectSelection",
|
||||
selection: NPSelection | ObjectNP | undefined,
|
||||
};
|
||||
|
||||
export type SubjectSelectionComplete = {
|
||||
type: "subjectSelectionComplete",
|
||||
selection: NPSelection,
|
||||
};
|
||||
|
||||
export type ObjectSelectionComplete = {
|
||||
type: "objectSelection",
|
||||
selection: NPSelection | ObjectNP,
|
||||
};
|
||||
|
||||
export type VPSelectionState = {
|
||||
// blocks: (SubjectSelection | ObjectSelection)[]
|
||||
subject: NPSelection | undefined,
|
||||
object: NPSelection | ObjectNP | undefined,
|
||||
verb: VerbSelection,
|
||||
form: FormVersion,
|
||||
};
|
||||
|
||||
export type VPSelectionComplete = {
|
||||
// blocks: (SubjectSelectionComplete | ObjectSelectionComplete)[]
|
||||
subject: NPSelection,
|
||||
object: NPSelection | ObjectNP,
|
||||
verb: VerbSelectionComplete,
|
||||
form: FormVersion,
|
||||
};
|
||||
|
||||
export type VerbSelectionComplete = Omit<VerbSelection, "object" | "verbTense" | "perfectTense" | "imperfectiveTense" | "tenseCategory"> & {
|
||||
object: Exclude<VerbObject, undefined>,
|
||||
tense: VerbTense | PerfectTense | ModalTense | ImperativeTense,
|
||||
}
|
||||
|
||||
|
@ -550,7 +574,6 @@ export type VerbSelection = {
|
|||
type: "verb",
|
||||
verb: VerbEntry,
|
||||
dynAuxVerb?: VerbEntry,
|
||||
object: VerbObject, // TODO: should have a locked in (but number changeable noun) here for dynamic compounds
|
||||
transitivity: Transitivity,
|
||||
canChangeTransitivity: boolean,
|
||||
canChangeStatDyn: boolean,
|
||||
|
@ -586,6 +609,8 @@ export type VerbObject =
|
|||
|
||||
export type NPSelection = NounSelection | PronounSelection | ParticipleSelection;
|
||||
|
||||
export type APSelection = AdverbSelection | SandwichSelection<Sandwich>;
|
||||
|
||||
export type NPType = "noun" | "pronoun" | "participle";
|
||||
|
||||
export type ObjectNP = "none" | Person.ThirdPlurMale;
|
||||
|
@ -608,6 +633,11 @@ export type NounSelection = {
|
|||
possesor: undefined | PossesorSelection,
|
||||
};
|
||||
|
||||
export type AdverbSelection = {
|
||||
type: "adverb",
|
||||
entry: AdverbEntry,
|
||||
}
|
||||
|
||||
export type AdjectiveSelection = {
|
||||
type: "adjective",
|
||||
entry: AdjectiveEntry,
|
||||
|
@ -645,23 +675,27 @@ export type RenderedPossesorSelection = {
|
|||
shrunken: boolean,
|
||||
};
|
||||
|
||||
export type Rendered<T extends NPSelection | EqCompSelection | AdjectiveSelection> = ReplaceKey<
|
||||
Omit<T, "changeGender" | "changeNumber" | "changeDistance" | "adjectives" | "possesor">,
|
||||
"e",
|
||||
string
|
||||
> & {
|
||||
ps: PsString[],
|
||||
e?: string,
|
||||
inflected: boolean,
|
||||
person: Person,
|
||||
role: "king" | "servant" | "none",
|
||||
// TODO: better recursive thing
|
||||
adjectives?: Rendered<AdjectiveSelection>[],
|
||||
possesor?: {
|
||||
shrunken: boolean,
|
||||
np: Rendered<NPSelection>,
|
||||
},
|
||||
};
|
||||
export type Rendered<T extends NPSelection | EqCompSelection | AdjectiveSelection | SandwichSelection<Sandwich>> = T extends SandwichSelection<Sandwich>
|
||||
? Omit<SandwichSelection<Sandwich>, "inside"> & {
|
||||
inside: Rendered<NPSelection>,
|
||||
}
|
||||
: ReplaceKey<
|
||||
Omit<T, "changeGender" | "changeNumber" | "changeDistance" | "adjectives" | "possesor">,
|
||||
"e",
|
||||
string
|
||||
> & {
|
||||
ps: PsString[],
|
||||
e?: string,
|
||||
inflected: boolean,
|
||||
person: Person,
|
||||
role: "king" | "servant" | "none",
|
||||
// TODO: better recursive thing
|
||||
adjectives?: Rendered<AdjectiveSelection>[],
|
||||
possesor?: {
|
||||
shrunken: boolean,
|
||||
np: Rendered<NPSelection>,
|
||||
},
|
||||
};
|
||||
// TODO: recursive changing this down into the possesor etc.
|
||||
|
||||
export type EPSelectionState = {
|
||||
|
@ -687,8 +721,19 @@ export type EPSelectionComplete = Omit<EPSelectionState, "subject" | "predicate"
|
|||
omitSubject: boolean,
|
||||
};
|
||||
|
||||
export type EqCompType = "adjective" | "loc. adv."; // TODO: - more
|
||||
export type EqCompSelection = AdjectiveSelection | LocativeAdverbSelection; // TODO: - more
|
||||
export type EqCompType = "adjective" | "loc. adv." | "sandwich"
|
||||
export type EqCompSelection = AdjectiveSelection | LocativeAdverbSelection | SandwichSelection<Sandwich>;
|
||||
|
||||
export type SandwichSelection<S extends Sandwich> = S & {
|
||||
inside: NPSelection,
|
||||
};
|
||||
|
||||
export type Sandwich = {
|
||||
type: "sandwich",
|
||||
before: PsString | undefined,
|
||||
after: PsString | undefined,
|
||||
e: string,
|
||||
};
|
||||
|
||||
export type EquativeSelection = {
|
||||
tense: EquativeTense,
|
||||
|
|
Loading…
Reference in New Issue