rough sandwich working
This commit is contained in:
parent
92b80c3b6a
commit
e8ec806ecb
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@lingdocs/pashto-inflector",
|
"name": "@lingdocs/pashto-inflector",
|
||||||
"version": "2.5.5",
|
"version": "2.5.6",
|
||||||
"author": "lingdocs.com",
|
"author": "lingdocs.com",
|
||||||
"description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations",
|
"description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations",
|
||||||
"homepage": "https://verbs.lingdocs.com",
|
"homepage": "https://verbs.lingdocs.com",
|
||||||
|
@ -76,7 +76,10 @@
|
||||||
"extends": [
|
"extends": [
|
||||||
"react-app",
|
"react-app",
|
||||||
"react-app/jest"
|
"react-app/jest"
|
||||||
]
|
],
|
||||||
|
"rules": {
|
||||||
|
"no-warning-comments": [1, {"terms": ["fixme", "xxx"], "location": "anywhere"}]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
|
|
|
@ -111,4 +111,69 @@ function EntrySelect<E extends T.DictionaryEntry | T.VerbEntry>(props: {
|
||||||
</div>
|
</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;
|
export default EntrySelect;
|
|
@ -2,7 +2,8 @@ import { useState, useEffect } from "react";
|
||||||
import * as T from "../../../types";
|
import * as T from "../../../types";
|
||||||
import AdjectivePicker from "../../np-picker/AdjectivePicker";
|
import AdjectivePicker from "../../np-picker/AdjectivePicker";
|
||||||
import LocativeAdverbPicker from "./LocativeAdverbPicker";
|
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: {
|
function EqCompPicker(props: {
|
||||||
onChange: (comp: T.EqCompSelection | undefined) => void,
|
onChange: (comp: T.EqCompSelection | undefined) => void,
|
||||||
|
@ -12,9 +13,13 @@ function EqCompPicker(props: {
|
||||||
heading?: JSX.Element | string,
|
heading?: JSX.Element | string,
|
||||||
entryFeeder: T.EntryFeeder,
|
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(() => {
|
useEffect(() => {
|
||||||
setCompType(props.comp ? props.comp.type : undefined);
|
setCompType(props.comp
|
||||||
|
? props.comp.type
|
||||||
|
: undefined);
|
||||||
}, [props.comp]);
|
}, [props.comp]);
|
||||||
function handleClear() {
|
function handleClear() {
|
||||||
setCompType(undefined);
|
setCompType(undefined);
|
||||||
|
@ -69,6 +74,15 @@ function EqCompPicker(props: {
|
||||||
opts={props.opts}
|
opts={props.opts}
|
||||||
onChange={props.onChange}
|
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}
|
: null}
|
||||||
</div>
|
</div>
|
||||||
</>;
|
</>;
|
||||||
|
|
|
@ -137,7 +137,7 @@ function NPPronounPicker({ onChange, pronoun, role, opts, is2ndPersonPicker }: {
|
||||||
const pSpec = is2ndPersonPicker
|
const pSpec = is2ndPersonPicker
|
||||||
? [pSpecA[1]]
|
? [pSpecA[1]]
|
||||||
: pSpecA;
|
: 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">
|
<div className="d-flex flex-row justify-content-between mb-2">
|
||||||
<ButtonSelect
|
<ButtonSelect
|
||||||
xSmall
|
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)) {
|
if (!("type" in VP)) {
|
||||||
return <div className="lead text-muted text-center mt-4">
|
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`;
|
return `Choose NP${twoNPs ? "s " : ""} to make a phrase`;
|
||||||
})()}
|
})()}
|
||||||
</div>;
|
</div>;
|
||||||
|
|
|
@ -67,10 +67,9 @@ function VPExplorer(props: {
|
||||||
}, 1500);
|
}, 1500);
|
||||||
}
|
}
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const newVps = makeVPSelectionState(props.verb, vps);
|
|
||||||
adjustVps({
|
adjustVps({
|
||||||
type: "load vps",
|
type: "set verb",
|
||||||
payload: newVps,
|
payload: props.verb,
|
||||||
});
|
});
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, [props.verb]);
|
}, [props.verb]);
|
||||||
|
@ -161,7 +160,7 @@ function VPExplorer(props: {
|
||||||
{mode === "phrases" ? <i className="fas fa-share-alt" /> : ""}
|
{mode === "phrases" ? <i className="fas fa-share-alt" /> : ""}
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<div className="text-center my-2">
|
||||||
<button onClick={handleSubjObjSwap} className="btn btn-sm btn-light">
|
<button onClick={handleSubjObjSwap} className="btn btn-sm btn-light">
|
||||||
<i className="fas fa-exchange-alt mr-2" /> subj/obj
|
<i className="fas fa-exchange-alt mr-2" /> subj/obj
|
||||||
|
@ -192,14 +191,14 @@ function VPExplorer(props: {
|
||||||
}
|
}
|
||||||
is2ndPersonPicker={vps.verb.tenseCategory === "imperative"}
|
is2ndPersonPicker={vps.verb.tenseCategory === "imperative"}
|
||||||
np={vps.subject}
|
np={vps.subject}
|
||||||
counterPart={vps.verb ? vps.verb.object : undefined}
|
counterPart={vps.verb ? vps.object : undefined}
|
||||||
onChange={handleSubjectChange}
|
onChange={handleSubjectChange}
|
||||||
opts={props.opts}
|
opts={props.opts}
|
||||||
isShrunk={(servantIsShrunk && roles.servant === "subject")}
|
isShrunk={(servantIsShrunk && roles.servant === "subject")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{vps.verb && (vps.verb.object !== "none") && <div className="my-2" style={{ background: (servantIsShrunk && roles.servant === "object") ? shrunkenBackground : "inherit" }}>
|
{vps.verb && (vps.object !== "none") && <div className="my-2" style={{ background: (servantIsShrunk && roles.servant === "object") ? shrunkenBackground : "inherit" }}>
|
||||||
{(typeof vps.verb.object === "number")
|
{(typeof vps.object === "number")
|
||||||
? <div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div>
|
? <div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div>
|
||||||
: <NPPicker
|
: <NPPicker
|
||||||
phraseIsComplete={phraseIsComplete}
|
phraseIsComplete={phraseIsComplete}
|
||||||
|
@ -218,7 +217,7 @@ function VPExplorer(props: {
|
||||||
</div>}
|
</div>}
|
||||||
entryFeeder={props.entryFeeder}
|
entryFeeder={props.entryFeeder}
|
||||||
role="object"
|
role="object"
|
||||||
np={vps.verb.object}
|
np={vps.object}
|
||||||
counterPart={vps.subject}
|
counterPart={vps.subject}
|
||||||
onChange={handleObjectChange}
|
onChange={handleObjectChange}
|
||||||
opts={props.opts}
|
opts={props.opts}
|
||||||
|
|
|
@ -375,8 +375,8 @@ function completeVPs(vps: T.VPSelectionState): T.VPSelectionComplete {
|
||||||
const oldSubj = vps.subject?.type === "pronoun"
|
const oldSubj = vps.subject?.type === "pronoun"
|
||||||
? vps.subject.person
|
? vps.subject.person
|
||||||
: undefined;
|
: undefined;
|
||||||
const oldObj = (typeof vps.verb.object === "object" && vps.verb.object.type === "pronoun")
|
const oldObj = (typeof vps.object === "object" && vps.object.type === "pronoun")
|
||||||
? vps.verb.object.person
|
? vps.object.person
|
||||||
: undefined;
|
: undefined;
|
||||||
const { subj, obj } = randomSubjObj(
|
const { subj, obj } = randomSubjObj(
|
||||||
oldSubj === undefined
|
oldSubj === undefined
|
||||||
|
@ -389,17 +389,6 @@ function completeVPs(vps: T.VPSelectionState): T.VPSelectionComplete {
|
||||||
const t = getTenseFromVerbSelection(vps.verb);
|
const t = getTenseFromVerbSelection(vps.verb);
|
||||||
const verb: T.VerbSelectionComplete = {
|
const verb: T.VerbSelectionComplete = {
|
||||||
...vps.verb,
|
...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,
|
tense: isImperativeTense(t) ? "presentVerb" : t,
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
|
@ -409,18 +398,29 @@ function completeVPs(vps: T.VPSelectionState): T.VPSelectionComplete {
|
||||||
distance: "far",
|
distance: "far",
|
||||||
person: subj,
|
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,
|
verb,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRandomVPSelection(mix: MixType = "both") {
|
function getRandomVPSelection(mix: MixType = "both") {
|
||||||
// TODO: Type safety to make sure it's safe?
|
// 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")
|
const oldSubj = (subject.type === "pronoun")
|
||||||
? subject.person
|
? subject.person
|
||||||
: undefined;
|
: undefined;
|
||||||
const oldObj = (typeof verb.object === "object" && verb.object.type === "pronoun")
|
const oldObj = (typeof object === "object" && object.type === "pronoun")
|
||||||
? verb.object.person
|
? object.person
|
||||||
: undefined;
|
: undefined;
|
||||||
const { subj, obj } = randomSubjObj(
|
const { subj, obj } = randomSubjObj(
|
||||||
oldSubj !== undefined ? { subj: oldSubj, obj: oldObj } : undefined
|
oldSubj !== undefined ? { subj: oldSubj, obj: oldObj } : undefined
|
||||||
|
@ -433,8 +433,8 @@ function getRandomVPSelection(mix: MixType = "both") {
|
||||||
distance: "far",
|
distance: "far",
|
||||||
person: subj,
|
person: subj,
|
||||||
};
|
};
|
||||||
const randObj: T.PronounSelection = typeof verb?.object === "object" && verb.object.type === "pronoun" ? {
|
const randObj: T.PronounSelection = typeof object === "object" && object.type === "pronoun" ? {
|
||||||
...verb.object,
|
...object,
|
||||||
person: obj,
|
person: obj,
|
||||||
} : {
|
} : {
|
||||||
type: "pronoun",
|
type: "pronoun",
|
||||||
|
@ -445,23 +445,21 @@ function getRandomVPSelection(mix: MixType = "both") {
|
||||||
if (mix === "tenses") {
|
if (mix === "tenses") {
|
||||||
return {
|
return {
|
||||||
subject: subject !== undefined ? subject : randSubj,
|
subject: subject !== undefined ? subject : randSubj,
|
||||||
|
object: object !== undefined ? object : randObj,
|
||||||
verb: randomizeTense(verb, true),
|
verb: randomizeTense(verb, true),
|
||||||
form: { removeKing: false, shrinkServant: false },
|
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 {
|
return {
|
||||||
subject: randSubj,
|
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 },
|
form: { removeKing: false, shrinkServant: false },
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,12 +15,12 @@ export function makeVPSelectionState(
|
||||||
function getTransObjFromos() {
|
function getTransObjFromos() {
|
||||||
if (
|
if (
|
||||||
!os ||
|
!os ||
|
||||||
os.verb.object === "none" ||
|
os.object === "none" ||
|
||||||
typeof os.verb.object === "number" ||
|
typeof os.object === "number" ||
|
||||||
os.verb.isCompound === "dynamic" ||
|
os.verb.isCompound === "dynamic" ||
|
||||||
(os.verb.object?.type === "noun" && os.verb.object.dynamicComplement)
|
(os.object?.type === "noun" && os.object.dynamicComplement)
|
||||||
) return undefined;
|
) return undefined;
|
||||||
return os.verb.object;
|
return os.object;
|
||||||
}
|
}
|
||||||
const transitivity: T.Transitivity = "grammaticallyTransitive" in info
|
const transitivity: T.Transitivity = "grammaticallyTransitive" in info
|
||||||
? "transitive"
|
? "transitive"
|
||||||
|
@ -47,6 +47,7 @@ export function makeVPSelectionState(
|
||||||
: undefined;
|
: undefined;
|
||||||
return {
|
return {
|
||||||
subject,
|
subject,
|
||||||
|
object,
|
||||||
verb: {
|
verb: {
|
||||||
type: "verb",
|
type: "verb",
|
||||||
verb: verb,
|
verb: verb,
|
||||||
|
@ -55,7 +56,6 @@ export function makeVPSelectionState(
|
||||||
perfectTense: os ? os.verb.perfectTense : "presentPerfect",
|
perfectTense: os ? os.verb.perfectTense : "presentPerfect",
|
||||||
imperativeTense: os ? os.verb.imperativeTense : "imperfectiveImperative",
|
imperativeTense: os ? os.verb.imperativeTense : "imperfectiveImperative",
|
||||||
tenseCategory: os ? os.verb.tenseCategory : "basic",
|
tenseCategory: os ? os.verb.tenseCategory : "basic",
|
||||||
object,
|
|
||||||
transitivity,
|
transitivity,
|
||||||
isCompound,
|
isCompound,
|
||||||
voice: transitivity === "transitive"
|
voice: transitivity === "transitive"
|
||||||
|
@ -70,27 +70,33 @@ export function makeVPSelectionState(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function changeStatDyn(v: T.VerbSelection, s: "dynamic" | "stative"): T.VerbSelection {
|
export function changeStatDyn(v: T.VPSelectionState, s: "dynamic" | "stative"): T.VPSelectionState {
|
||||||
const info = getVerbInfo(v.verb.entry, v.verb.complement);
|
const info = getVerbInfo(v.verb.verb.entry, v.verb.verb.complement);
|
||||||
if (!("stative" in info)) {
|
if (!("stative" in info)) {
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...v,
|
...v,
|
||||||
isCompound: s,
|
|
||||||
object: s === "dynamic"
|
object: s === "dynamic"
|
||||||
? makeNounSelection(info.dynamic.objComplement.entry as T.NounEntry, undefined, true)
|
? makeNounSelection(info.dynamic.objComplement.entry as T.NounEntry, undefined, true)
|
||||||
: undefined,
|
: undefined,
|
||||||
dynAuxVerb: s === "dynamic"
|
verb: {
|
||||||
? { entry: info.dynamic.auxVerb } as T.VerbEntry
|
...v.verb,
|
||||||
: undefined,
|
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 {
|
return {
|
||||||
...v,
|
...v,
|
||||||
transitivity,
|
|
||||||
object: transitivity === "grammatically transitive" ? T.Person.ThirdPlurMale : undefined,
|
object: transitivity === "grammatically transitive" ? T.Person.ThirdPlurMale : undefined,
|
||||||
|
verb: {
|
||||||
|
...v.verb,
|
||||||
|
transitivity,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,9 @@ import {
|
||||||
isImperativeTense,
|
isImperativeTense,
|
||||||
} from "../../lib/type-predicates";
|
} from "../../lib/type-predicates";
|
||||||
import { checkForMiniPronounsError } from "../../lib/phrase-building/compile";
|
import { checkForMiniPronounsError } from "../../lib/phrase-building/compile";
|
||||||
|
import {
|
||||||
|
makeVPSelectionState,
|
||||||
|
} from "./verb-selection";
|
||||||
|
|
||||||
export type VpsReducerAction = {
|
export type VpsReducerAction = {
|
||||||
type: "load vps",
|
type: "load vps",
|
||||||
|
@ -48,7 +51,10 @@ export type VpsReducerAction = {
|
||||||
payload: "basic" | "modal" | "perfect" | "imperative",
|
payload: "basic" | "modal" | "perfect" | "imperative",
|
||||||
} | {
|
} | {
|
||||||
type: "toggle servant shrink",
|
type: "toggle servant shrink",
|
||||||
};
|
} | {
|
||||||
|
type: "set verb",
|
||||||
|
payload: T.VerbEntry,
|
||||||
|
}
|
||||||
|
|
||||||
export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, sendAlert?: (msg: string) => void): T.VPSelectionState {
|
export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, sendAlert?: (msg: string) => void): T.VPSelectionState {
|
||||||
return ensureMiniPronounsOk(vps, doReduce());
|
return ensureMiniPronounsOk(vps, doReduce());
|
||||||
|
@ -70,7 +76,7 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
|
||||||
if (
|
if (
|
||||||
!skipPronounConflictCheck
|
!skipPronounConflictCheck
|
||||||
&&
|
&&
|
||||||
hasPronounConflict(subject, vps.verb?.object)
|
hasPronounConflict(subject, vps.object)
|
||||||
) {
|
) {
|
||||||
if (sendAlert) sendAlert("That combination of pronouns is not allowed");
|
if (sendAlert) sendAlert("That combination of pronouns is not allowed");
|
||||||
return vps;
|
return vps;
|
||||||
|
@ -82,7 +88,7 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
|
||||||
}
|
}
|
||||||
if (action.type === "set object") {
|
if (action.type === "set object") {
|
||||||
if (!vps.verb) return vps;
|
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;
|
return vps;
|
||||||
}
|
}
|
||||||
const object = action.payload;
|
const object = action.payload;
|
||||||
|
@ -93,10 +99,7 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...vps,
|
...vps,
|
||||||
verb: {
|
object,
|
||||||
...vps.verb,
|
|
||||||
object,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (action.type === "swap subj/obj") {
|
if (action.type === "swap subj/obj") {
|
||||||
|
@ -118,10 +121,10 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
|
||||||
if (voice === "passive") {
|
if (voice === "passive") {
|
||||||
return {
|
return {
|
||||||
...vps,
|
...vps,
|
||||||
subject: typeof vps.verb.object === "object" ? vps.verb.object : undefined,
|
subject: typeof vps.object === "object" ? vps.object : undefined,
|
||||||
|
object: "none",
|
||||||
verb: {
|
verb: {
|
||||||
...vps.verb,
|
...vps.verb,
|
||||||
object: "none",
|
|
||||||
voice,
|
voice,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -129,10 +132,9 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
|
||||||
return {
|
return {
|
||||||
...vps,
|
...vps,
|
||||||
subject: undefined,
|
subject: undefined,
|
||||||
|
object: typeof vps.subject === "object" ? vps.subject : undefined,
|
||||||
verb: {
|
verb: {
|
||||||
...vps.verb,
|
...vps.verb,
|
||||||
// TODO: is this ok??
|
|
||||||
object: typeof vps.subject === "object" ? vps.subject : undefined,
|
|
||||||
voice,
|
voice,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -143,17 +145,11 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
|
||||||
}
|
}
|
||||||
if (action.type === "set transitivity") {
|
if (action.type === "set transitivity") {
|
||||||
if (!(vps.verb && vps.verb.canChangeTransitivity)) return vps;
|
if (!(vps.verb && vps.verb.canChangeTransitivity)) return vps;
|
||||||
return {
|
return changeTransitivity(vps, action.payload);
|
||||||
...vps,
|
|
||||||
verb: changeTransitivity(vps.verb, action.payload),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
if (action.type === "set statDyn") {
|
if (action.type === "set statDyn") {
|
||||||
if (!(vps.verb && vps.verb.canChangeStatDyn)) return vps;
|
if (!(vps.verb && vps.verb.canChangeStatDyn)) return vps;
|
||||||
return {
|
return changeStatDyn(vps, action.payload);
|
||||||
...vps,
|
|
||||||
verb: changeStatDyn(vps.verb, action.payload),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
if (action.type === "set negativity") {
|
if (action.type === "set negativity") {
|
||||||
if (!vps.verb) return vps;
|
if (!vps.verb) return vps;
|
||||||
|
@ -220,14 +216,17 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// if (action.type === "toggle servant shrink") {
|
if (action.type === "toggle servant shrink") {
|
||||||
return {
|
return {
|
||||||
...vps,
|
...vps,
|
||||||
form: {
|
form: {
|
||||||
...vps.form,
|
...vps.form,
|
||||||
shrinkServant: !vps.form.shrinkServant,
|
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";
|
import { concatPsString } from "../p-text-helpers";
|
||||||
|
|
||||||
function getBaseAndAdjectives(np: T.Rendered<T.NPSelection | T.EqCompSelection>): T.PsString[] {
|
function getBaseAndAdjectives(np: T.Rendered<T.NPSelection | T.EqCompSelection>): T.PsString[] {
|
||||||
|
if (np.type === "sandwich") {
|
||||||
|
return getSandwichPsBaseAndAdjectives(np);
|
||||||
|
}
|
||||||
const adjs = np.adjectives;
|
const adjs = np.adjectives;
|
||||||
if (!adjs) {
|
if (!adjs) {
|
||||||
return np.ps;
|
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> {
|
function trimOffShrunkenPossesive(p: T.Rendered<T.NPSelection>): T.Rendered<T.NPSelection> {
|
||||||
if (!("possesor" in p)) {
|
if (!("possesor" in p)) {
|
||||||
return 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[] {
|
export function getPashtoFromRendered(np: T.Rendered<T.NPSelection> | T.Rendered<T.EqCompSelection>, subjectsPerson: false | T.Person): T.PsString[] {
|
||||||
const base = getBaseAndAdjectives(np);
|
const base = getBaseAndAdjectives(np);
|
||||||
if (np.type !== "loc. adv." && np.type !== "adjective") {
|
if (np.type === "loc. adv." || np.type === "adjective") {
|
||||||
// ts being dumb
|
return base;
|
||||||
const trimmed = trimOffShrunkenPossesive(np as T.Rendered<T.NPSelection>);
|
}
|
||||||
if (trimmed.possesor) {
|
const trimmed = np.type === "sandwich" ? {
|
||||||
return addPossesor(trimmed.possesor.np, base, subjectsPerson);
|
...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;
|
return base;
|
||||||
}
|
}
|
||||||
|
@ -143,6 +180,9 @@ function pronounPossEng(p: T.Person): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getEnglishFromRendered(r: T.Rendered<T.NPSelection | T.EqCompSelection>): string | undefined {
|
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.e) return undefined;
|
||||||
if (r.type === "loc. adv." || r.type === "adjective") {
|
if (r.type === "loc. adv." || r.type === "adjective") {
|
||||||
return r.e;
|
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!
|
// TODO: possesives in English for participles and pronouns too!
|
||||||
return r.e;
|
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 { psStringFromEntry } from "../p-text-helpers";
|
||||||
import { isLocativeAdverbEntry } from "../type-predicates";
|
import { isLocativeAdverbEntry } from "../type-predicates";
|
||||||
import { renderAdjectiveSelection } from "./render-adj";
|
import { renderAdjectiveSelection } from "./render-adj";
|
||||||
|
import { renderSandwich } from "./render-sandwich";
|
||||||
|
|
||||||
export function renderEP(EP: T.EPSelectionComplete): T.EPRendered {
|
export function renderEP(EP: T.EPSelectionComplete): T.EPRendered {
|
||||||
const king = (EP.subject.type === "pronoun")
|
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> {
|
function renderEqCompSelection(s: T.EqCompSelection, person: T.Person): T.Rendered<T.EqCompSelection> {
|
||||||
|
if (s.type === "sandwich") {
|
||||||
|
return renderSandwich(s);
|
||||||
|
}
|
||||||
const e = getEnglishWord(s.entry);
|
const e = getEnglishWord(s.entry);
|
||||||
if (!e || typeof e !== "string") {
|
if (!e || typeof e !== "string") {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {
|
||||||
} from "../../lib/np-tools";
|
} from "../../lib/np-tools";
|
||||||
import { getEnglishWord } from "../get-english-word";
|
import { getEnglishWord } from "../get-english-word";
|
||||||
import { renderAdjectiveSelection } from "./render-adj";
|
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, 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: "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;
|
if (!possesor) return undefined;
|
||||||
const isSingUnisexAnim5PatternNoun = (possesor.np.type === "noun"
|
const isSingUnisexAnim5PatternNoun = (possesor.np.type === "noun"
|
||||||
&& possesor.np.number === "singular"
|
&& possesor.np.number === "singular"
|
||||||
&& isUnisexAnimNounEntry(possesor.np.entry)
|
&& isAnimNounEntry(possesor.np.entry)
|
||||||
&& isPattern5Entry(possesor.np.entry)
|
&& isPattern5Entry(possesor.np.entry)
|
||||||
);
|
);
|
||||||
return {
|
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 {
|
export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
|
||||||
// Sentence Rules Logic
|
// Sentence Rules Logic
|
||||||
const isPast = isPastTense(VP.verb.tense);
|
const isPast = isPastTense(VP.verb.tense);
|
||||||
const isTransitive = VP.verb.object !== "none";
|
const isTransitive = VP.object !== "none";
|
||||||
const { king, servant } = getKingAndServant(isPast, isTransitive);
|
const { king, servant } = getKingAndServant(isPast, isTransitive);
|
||||||
const kingPerson = getPersonFromNP(
|
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
|
// TODO: more elegant way of handling this type safety
|
||||||
if (kingPerson === undefined) {
|
if (kingPerson === undefined) {
|
||||||
throw new Error("king of sentance does not exist");
|
throw new Error("king of sentance does not exist");
|
||||||
}
|
}
|
||||||
const subjectPerson = getPersonFromNP(VP.subject);
|
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
|
// TODO: also don't inflect if it's a pattern one animate noun
|
||||||
const inflectSubject = isPast && isTransitive && !isMascSingAnimatePattern4(VP.subject);
|
const inflectSubject = isPast && isTransitive && !isMascSingAnimatePattern4(VP.subject);
|
||||||
const inflectObject = !isPast && isFirstOrSecondPersPronoun(VP.verb.object);
|
const inflectObject = !isPast && isFirstOrSecondPersPronoun(VP.object);
|
||||||
// Render Elements
|
// Render Elements
|
||||||
const b: T.VPRendered = {
|
const b: T.VPRendered = {
|
||||||
type: "VPRendered",
|
type: "VPRendered",
|
||||||
|
@ -51,11 +51,11 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
|
||||||
isTransitive,
|
isTransitive,
|
||||||
isCompound: VP.verb.isCompound,
|
isCompound: VP.verb.isCompound,
|
||||||
subject: renderNPSelection(VP.subject, inflectSubject, false, "subject", king === "subject" ? "king" : "servant"),
|
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),
|
verb: renderVerbSelection(VP.verb, kingPerson, objectPerson),
|
||||||
englishBase: renderEnglishVPBase({
|
englishBase: renderEnglishVPBase({
|
||||||
subjectPerson,
|
subjectPerson,
|
||||||
object: VP.verb.isCompound === "dynamic" ? "none" : VP.verb.object,
|
object: VP.verb.isCompound === "dynamic" ? "none" : VP.object,
|
||||||
vs: VP.verb,
|
vs: VP.verb,
|
||||||
}),
|
}),
|
||||||
form: VP.form,
|
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.VPSelectionComplete): T.VPSelectionComplete;
|
||||||
export function switchSubjObj(vps: T.VPSelectionState | T.VPSelectionComplete): T.VPSelectionState | T.VPSelectionComplete {
|
export function switchSubjObj(vps: T.VPSelectionState | T.VPSelectionComplete): T.VPSelectionState | T.VPSelectionComplete {
|
||||||
if ("tenseCategory" in vps.verb) {
|
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;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...vps,
|
...vps,
|
||||||
subject: vps.verb.object,
|
subject: vps.object,
|
||||||
verb: {
|
object: vps.subject,
|
||||||
...vps.verb,
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...vps,
|
...vps,
|
||||||
subject: vps.verb.object,
|
subject: vps.object,
|
||||||
verb: {
|
object: vps.subject,
|
||||||
...vps.verb,
|
|
||||||
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 {
|
export function completeVPSelection(vps: T.VPSelectionState): T.VPSelectionComplete | undefined {
|
||||||
if (vps.subject === undefined) {
|
if (vps.subject === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (vps.verb.object === undefined) {
|
if (vps.object === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
// necessary for this version on typscript ...
|
// necessary for this version on typscript ...
|
||||||
const verb: T.VerbSelectionComplete = {
|
const verb: T.VerbSelectionComplete = {
|
||||||
...vps.verb,
|
...vps.verb,
|
||||||
object: vps.verb.object,
|
|
||||||
tense: getTenseFromVerbSelection(vps.verb),
|
tense: getTenseFromVerbSelection(vps.verb),
|
||||||
};
|
};
|
||||||
const subject = vps.subject;
|
const subject = vps.subject;
|
||||||
|
const object = vps.object
|
||||||
return {
|
return {
|
||||||
...vps,
|
...vps,
|
||||||
subject,
|
subject,
|
||||||
|
object,
|
||||||
verb,
|
verb,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -264,9 +301,9 @@ export function isThirdPerson(p: T.Person): boolean {
|
||||||
export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState): T.VPSelectionState {
|
export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState): T.VPSelectionState {
|
||||||
console.log("checking more...", vps);
|
console.log("checking more...", vps);
|
||||||
const subjIs2ndPerson = (vps.subject?.type === "pronoun") && isSecondPerson(vps.subject.person);
|
const subjIs2ndPerson = (vps.subject?.type === "pronoun") && isSecondPerson(vps.subject.person);
|
||||||
const objIs2ndPerson = (typeof vps.verb.object === "object")
|
const objIs2ndPerson = (typeof vps.object === "object")
|
||||||
&& (vps.verb.object.type === "pronoun")
|
&& (vps.object.type === "pronoun")
|
||||||
&& isSecondPerson(vps.verb.object.person);
|
&& isSecondPerson(vps.object.person);
|
||||||
console.log({ subjIs2ndPerson, objIs2ndPerson });
|
console.log({ subjIs2ndPerson, objIs2ndPerson });
|
||||||
const default2ndPersSubject: T.PronounSelection = {
|
const default2ndPersSubject: T.PronounSelection = {
|
||||||
type: "pronoun",
|
type: "pronoun",
|
||||||
|
@ -284,22 +321,19 @@ export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState):
|
||||||
return vps;
|
return vps;
|
||||||
}
|
}
|
||||||
if (subjIs2ndPerson && objIs2ndPerson) {
|
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;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...vps,
|
...vps,
|
||||||
verb: {
|
object: {
|
||||||
...vps.verb,
|
...vps.object,
|
||||||
object: {
|
person: getNon2ndPersPronoun(),
|
||||||
...vps.verb.object,
|
|
||||||
person: getNon2ndPersPronoun(),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (!subjIs2ndPerson && objIs2ndPerson) {
|
if (!subjIs2ndPerson && objIs2ndPerson) {
|
||||||
if (typeof vps.verb.object !== "object" || vps.verb.object.type !== "pronoun") {
|
if (typeof vps.object !== "object" || vps.object.type !== "pronoun") {
|
||||||
return {
|
return {
|
||||||
...vps,
|
...vps,
|
||||||
subject: default2ndPersSubject,
|
subject: default2ndPersSubject,
|
||||||
|
@ -308,12 +342,9 @@ export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelectionState):
|
||||||
return {
|
return {
|
||||||
...vps,
|
...vps,
|
||||||
subject: default2ndPersSubject,
|
subject: default2ndPersSubject,
|
||||||
verb: {
|
object: {
|
||||||
...vps.verb,
|
...vps.object,
|
||||||
object: {
|
person: getNon2ndPersPronoun(),
|
||||||
...vps.verb.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");
|
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 {
|
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) {
|
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 {
|
export function isImperativeTense(tense: T.Tense): tense is T.ImperativeTense {
|
||||||
return tense === "imperfectiveImperative" || tense === "perfectiveImperative";
|
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 NounEntry = DictionaryEntry & { c: string } & { __brand: "a noun entry" };
|
||||||
export type MascNounEntry = NounEntry & { __brand2: "a masc noun entry" };
|
export type MascNounEntry = NounEntry & { __brand2: "a masc noun entry" };
|
||||||
export type FemNounEntry = NounEntry & { __brand2: "a fem 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 UnisexNounEntry = MascNounEntry & { __brand3: "a unisex noun entry" };
|
||||||
export type UnisexAnimNounEntry = UnisexNounEntry & { __brand4: "an anim unisex noun entry" };
|
export type UnisexAnimNounEntry = UnisexNounEntry & { __brand4: "an anim unisex noun entry" };
|
||||||
export type AdverbEntry = DictionaryEntry & { c: string } & { __brand: "an adverb 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 ImperativeTense = `${Aspect}Imperative`;
|
||||||
export type Tense = EquativeTense | VerbTense | PerfectTense | ModalTense | ImperativeTense;
|
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 = {
|
export type VPSelectionState = {
|
||||||
|
// blocks: (SubjectSelection | ObjectSelection)[]
|
||||||
subject: NPSelection | undefined,
|
subject: NPSelection | undefined,
|
||||||
|
object: NPSelection | ObjectNP | undefined,
|
||||||
verb: VerbSelection,
|
verb: VerbSelection,
|
||||||
form: FormVersion,
|
form: FormVersion,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type VPSelectionComplete = {
|
export type VPSelectionComplete = {
|
||||||
|
// blocks: (SubjectSelectionComplete | ObjectSelectionComplete)[]
|
||||||
subject: NPSelection,
|
subject: NPSelection,
|
||||||
|
object: NPSelection | ObjectNP,
|
||||||
verb: VerbSelectionComplete,
|
verb: VerbSelectionComplete,
|
||||||
form: FormVersion,
|
form: FormVersion,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type VerbSelectionComplete = Omit<VerbSelection, "object" | "verbTense" | "perfectTense" | "imperfectiveTense" | "tenseCategory"> & {
|
export type VerbSelectionComplete = Omit<VerbSelection, "object" | "verbTense" | "perfectTense" | "imperfectiveTense" | "tenseCategory"> & {
|
||||||
object: Exclude<VerbObject, undefined>,
|
|
||||||
tense: VerbTense | PerfectTense | ModalTense | ImperativeTense,
|
tense: VerbTense | PerfectTense | ModalTense | ImperativeTense,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -550,7 +574,6 @@ export type VerbSelection = {
|
||||||
type: "verb",
|
type: "verb",
|
||||||
verb: VerbEntry,
|
verb: VerbEntry,
|
||||||
dynAuxVerb?: VerbEntry,
|
dynAuxVerb?: VerbEntry,
|
||||||
object: VerbObject, // TODO: should have a locked in (but number changeable noun) here for dynamic compounds
|
|
||||||
transitivity: Transitivity,
|
transitivity: Transitivity,
|
||||||
canChangeTransitivity: boolean,
|
canChangeTransitivity: boolean,
|
||||||
canChangeStatDyn: boolean,
|
canChangeStatDyn: boolean,
|
||||||
|
@ -586,6 +609,8 @@ export type VerbObject =
|
||||||
|
|
||||||
export type NPSelection = NounSelection | PronounSelection | ParticipleSelection;
|
export type NPSelection = NounSelection | PronounSelection | ParticipleSelection;
|
||||||
|
|
||||||
|
export type APSelection = AdverbSelection | SandwichSelection<Sandwich>;
|
||||||
|
|
||||||
export type NPType = "noun" | "pronoun" | "participle";
|
export type NPType = "noun" | "pronoun" | "participle";
|
||||||
|
|
||||||
export type ObjectNP = "none" | Person.ThirdPlurMale;
|
export type ObjectNP = "none" | Person.ThirdPlurMale;
|
||||||
|
@ -608,6 +633,11 @@ export type NounSelection = {
|
||||||
possesor: undefined | PossesorSelection,
|
possesor: undefined | PossesorSelection,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AdverbSelection = {
|
||||||
|
type: "adverb",
|
||||||
|
entry: AdverbEntry,
|
||||||
|
}
|
||||||
|
|
||||||
export type AdjectiveSelection = {
|
export type AdjectiveSelection = {
|
||||||
type: "adjective",
|
type: "adjective",
|
||||||
entry: AdjectiveEntry,
|
entry: AdjectiveEntry,
|
||||||
|
@ -645,23 +675,27 @@ export type RenderedPossesorSelection = {
|
||||||
shrunken: boolean,
|
shrunken: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Rendered<T extends NPSelection | EqCompSelection | AdjectiveSelection> = ReplaceKey<
|
export type Rendered<T extends NPSelection | EqCompSelection | AdjectiveSelection | SandwichSelection<Sandwich>> = T extends SandwichSelection<Sandwich>
|
||||||
Omit<T, "changeGender" | "changeNumber" | "changeDistance" | "adjectives" | "possesor">,
|
? Omit<SandwichSelection<Sandwich>, "inside"> & {
|
||||||
"e",
|
inside: Rendered<NPSelection>,
|
||||||
string
|
}
|
||||||
> & {
|
: ReplaceKey<
|
||||||
ps: PsString[],
|
Omit<T, "changeGender" | "changeNumber" | "changeDistance" | "adjectives" | "possesor">,
|
||||||
e?: string,
|
"e",
|
||||||
inflected: boolean,
|
string
|
||||||
person: Person,
|
> & {
|
||||||
role: "king" | "servant" | "none",
|
ps: PsString[],
|
||||||
// TODO: better recursive thing
|
e?: string,
|
||||||
adjectives?: Rendered<AdjectiveSelection>[],
|
inflected: boolean,
|
||||||
possesor?: {
|
person: Person,
|
||||||
shrunken: boolean,
|
role: "king" | "servant" | "none",
|
||||||
np: Rendered<NPSelection>,
|
// TODO: better recursive thing
|
||||||
},
|
adjectives?: Rendered<AdjectiveSelection>[],
|
||||||
};
|
possesor?: {
|
||||||
|
shrunken: boolean,
|
||||||
|
np: Rendered<NPSelection>,
|
||||||
|
},
|
||||||
|
};
|
||||||
// TODO: recursive changing this down into the possesor etc.
|
// TODO: recursive changing this down into the possesor etc.
|
||||||
|
|
||||||
export type EPSelectionState = {
|
export type EPSelectionState = {
|
||||||
|
@ -687,8 +721,19 @@ export type EPSelectionComplete = Omit<EPSelectionState, "subject" | "predicate"
|
||||||
omitSubject: boolean,
|
omitSubject: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EqCompType = "adjective" | "loc. adv."; // TODO: - more
|
export type EqCompType = "adjective" | "loc. adv." | "sandwich"
|
||||||
export type EqCompSelection = AdjectiveSelection | LocativeAdverbSelection; // TODO: - more
|
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 = {
|
export type EquativeSelection = {
|
||||||
tense: EquativeTense,
|
tense: EquativeTense,
|
||||||
|
|
Loading…
Reference in New Issue