big improvements and equative explorer/machine working!
This commit is contained in:
parent
9b0c451458
commit
b6ebdd66ca
|
@ -0,0 +1,53 @@
|
|||
module.exports = [
|
||||
{ ts: 1527822780, e: `forward, towards` }, // ورمخته - wărmukhta
|
||||
{ ts: 1527820292, e: `behind` }, // شاته - shaata
|
||||
{ ts: 1527814912, e: `near, close to` }, // رانږدې - raanaGde
|
||||
{ ts: 1527813928, e: `right here, in this very place` }, // همدلته - hamdalta
|
||||
{ ts: 1610454983695, e: `at this place, at that place, here` }, // دغلته - daghalta
|
||||
{ ts: 1527813279, e: `in front of, forward, ahead of` }, // مخته - mukhta
|
||||
{ ts: 1527822042, e: `above, overhead` }, // راپورته - raaporta
|
||||
{ ts: 1588758935200, e: `that way, direction, over there` }, // وراخوا - wăraakhwaa
|
||||
{ ts: 1527817302, e: `together, in one place` }, // یوځای - yodzaay
|
||||
{ ts: 1527812558, e: `here` }, // دلته - dălta
|
||||
{ ts: 1527814911, e: `near, close to, almost` }, // نږدې - naGde
|
||||
{ ts: 1578080952673, e: `outside, outside of, beyond` }, // دباندې - dubaande
|
||||
{ ts: 1527812780, e: `down, beneath` }, // ښکته - xkuta
|
||||
{ ts: 1527813688, e: `lower, below, down` }, // کوز - kooz
|
||||
{ ts: 1610796256372, e: `forward` }, // مخ په وړاندې - mukh pu wRaande
|
||||
{ ts: 1527812293, e: `with you` }, // درسره - dărsara
|
||||
{ ts: 1527812935, e: `face to face, across from, opposite; straight` }, // مخامخ - mukhaamukh
|
||||
{ ts: 1527815420, e: `later, after, behind` }, // وروسته - wroosta
|
||||
{ ts: 1527811221, e: `above, overhead` }, // پورته - porta
|
||||
{ ts: 1527813690, e: `above, on top` }, // بر - bar
|
||||
{ ts: 1527822803, e: `far, distantly, a bit further, to the side, back, away, direction` }, // هیسته - héesta
|
||||
{ ts: 1579824262965, e: `together, in one place; at the same time` }, // یو ځای - yo dzaay
|
||||
{ ts: 1527814913, e: `near, close to` }, // ورنږدې - warnaGde
|
||||
{ ts: 1527814318, e: `in front, forward, before` }, // مخکښې - mukhkxe
|
||||
{ ts: 1527812413, e: `after oneself` }, // راپسې - raapase
|
||||
{ ts: 1527822852, e: `this way and that way, here and there` }, // دېخوا هاخوا - dekhwaa haakhwaa
|
||||
{ ts: 1527812449, e: `there` }, // هلته - hálta, álta
|
||||
{ ts: 1610796249240, e: `forward` }, // مخ پر وړاندې - mukh pur wRaande
|
||||
{ ts: 1527819393, e: `over/across to you/there` }, // درپورې - darpore
|
||||
{ ts: 1527821253, e: `near, close (نژدې)` }, // نزدې - nizde, nazde
|
||||
{ ts: 1527813122, e: `inside, within, interior` }, // دننه - dununa
|
||||
{ ts: 1527817710, e: `that side, across` }, // ها خوا - haakhwaa
|
||||
{ ts: 1581189430959, e: `ahead, in front; earlier, first, before` }, // پېش - pesh
|
||||
{ ts: 1527823145, e: `outside, from without, on the outside; abroad (as in out of country)` }, // بېرون - beróon
|
||||
{ ts: 1527814605, e: `far, distant` }, // لرې - lure
|
||||
{ ts: 1527819438, e: `far, distant, removed` }, // لېرې - lere
|
||||
{ ts: 1578015603537, e: `above` }, // له پاسه - la paasa
|
||||
{ ts: 1588780597931, e: `near, close to, almost` }, // نېږدې - neGdé
|
||||
{ ts: 1527814708, e: `close, near, soon, almost` }, // نژدې - nijzde, najzde
|
||||
{ ts: 1594909066356, e: `around, in the area` }, // خوا و شا - khwaa-U-shaa
|
||||
{ ts: 1577999518050, e: `there, over there, thither` }, // هورې - hooré
|
||||
{ ts: 1527815416, e: `ahead, forward, before, earlier` }, // وړاندې - wRaande
|
||||
{ ts: 1527818064, e: `after him/her/it/them, behind` }, // ورپسې - warpase
|
||||
{ ts: 1527818186, e: `after you (sing and plural)` }, // درپسې - dărpase
|
||||
{ ts: 1527818069, e: `in front of him/her/it/them` }, // وروړاندې - wărwRaande
|
||||
{ ts: 1527819321, e: `down, below; slope` }, // کښته - kxúta
|
||||
{ ts: 1527820805, e: `right there, in the same place, in the very same place` }, // هماغلته - hamáaghalta
|
||||
{ ts: 1527815457, e: `all over, throughout` }, // سراسري - săraasaree
|
||||
{ ts: 1527819388, e: `inside, in` }, // وردننه - wardunúna
|
||||
{ ts: 1527812375, e: `back (as in return)` }, // واپس - waapus
|
||||
{ ts: 1527817294, e: `up, above` }, // پاس - paas
|
||||
];
|
|
@ -5,7 +5,6 @@ module.exports = [
|
|||
{ ts: 1588857967561, e: `bull` }, // غوایه - ghwaayú
|
||||
{ ts: 1527817108, e: `look, gaze, examination, inspection, spying` }, // کاته - kaatu
|
||||
{ ts: 1527817768, e: `raven, crow` }, // کارګه - kaargu
|
||||
{ ts: 1527819245, e: `master of house, head of family, married man` }, // کوربانه - korbaanú
|
||||
{ ts: 1527818516, e: `swimming, bathing` }, // لمبېده - lambedú
|
||||
{ ts: 1527813986, e: `sunset, west` }, // لمر پرېواته - lmarprewaatu
|
||||
{ ts: 1527813992, e: `sunset` }, // لمر لوېده - lmarlwedu
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@lingdocs/pashto-inflector",
|
||||
"version": "2.2.0",
|
||||
"version": "2.3.0",
|
||||
"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",
|
||||
|
|
17
src/App.tsx
17
src/App.tsx
|
@ -23,7 +23,7 @@ import {
|
|||
Modal
|
||||
} from "react-bootstrap";
|
||||
import * as T from "./types";
|
||||
import { isNounEntry } from "./lib/type-predicates";
|
||||
import { isAdjectiveEntry, isLocativeAdverbEntry, isNounEntry } from "./lib/type-predicates";
|
||||
import defualtTextOptions from "./lib/default-text-options";
|
||||
import PhraseBuilder from "./components/vp-explorer/VPExplorer";
|
||||
import useStickyState from "./lib/useStickyState";
|
||||
|
@ -35,6 +35,14 @@ const verbTypes: VerbType[] = [
|
|||
];
|
||||
|
||||
const nouns = nounsAdjs.filter(isNounEntry);
|
||||
const adjectives = nounsAdjs.filter(isAdjectiveEntry);
|
||||
const locativeAdverbs = nounsAdjs.filter(isLocativeAdverbEntry);
|
||||
const entryFeeder: T.EntryFeeder = {
|
||||
locativeAdverbs,
|
||||
nouns,
|
||||
adjectives,
|
||||
verbs,
|
||||
};
|
||||
|
||||
const transitivities: T.Transitivity[] = [
|
||||
"transitive",
|
||||
|
@ -47,6 +55,8 @@ const allVerbs = verbs.map((v: { entry: T.DictionaryEntry, complement?: T.Dictio
|
|||
info: getVerbInfo(v.entry, v.complement),
|
||||
}));
|
||||
|
||||
|
||||
|
||||
function App() {
|
||||
const [verbTs, setVerbTs] = useStickyState<number>(0, "verbTs1");
|
||||
const [verbTypeShowing, setVerbTypeShowing] = useStickyState<VerbType>("simple", "vTypeShowing");
|
||||
|
@ -215,6 +225,7 @@ function App() {
|
|||
name="verb-type"
|
||||
checked={verbTypeShowing === type}
|
||||
value={type}
|
||||
onChange={() => null}
|
||||
/>
|
||||
<label className="form-check-label">
|
||||
{type}
|
||||
|
@ -244,6 +255,7 @@ function App() {
|
|||
type="radio"
|
||||
name="transitivity"
|
||||
checked={transitivityShowing === transitivity}
|
||||
onChange={() => null}
|
||||
value={transitivity}
|
||||
/>
|
||||
<label className="form-check-label">
|
||||
|
@ -260,8 +272,7 @@ function App() {
|
|||
<PhraseBuilder
|
||||
handleLinkClick="none"
|
||||
verb={v.verb as T.VerbEntry}
|
||||
nouns={nouns}
|
||||
verbs={verbs}
|
||||
entryFeeder={entryFeeder}
|
||||
opts={textOptions}
|
||||
/>
|
||||
</div>}
|
||||
|
|
|
@ -27,12 +27,8 @@ export const customStyles: StylesConfig = {
|
|||
}),
|
||||
}
|
||||
|
||||
function EntrySelect<E extends T.DictionaryEntry | T.VerbEntry>(props: ({
|
||||
entries: E[]
|
||||
} | {
|
||||
searchF: (search: string) => E[],
|
||||
getByTs: (ts: number) => E | undefined,
|
||||
}) & {
|
||||
function EntrySelect<E extends T.DictionaryEntry | T.VerbEntry>(props: {
|
||||
entryFeeder: T.EntryFeederSingleType<E>,
|
||||
value: E | undefined,
|
||||
onChange: (value: E | undefined) => void,
|
||||
name: string | undefined,
|
||||
|
@ -49,17 +45,19 @@ function EntrySelect<E extends T.DictionaryEntry | T.VerbEntry>(props: ({
|
|||
return makeSelectOption(e, props.opts);
|
||||
}
|
||||
const value = props.value ? makeOption(props.value) : undefined;
|
||||
if ("searchF" in props) {
|
||||
if ("search" in props.entryFeeder) {
|
||||
const search = props.entryFeeder.search;
|
||||
const getByTs = props.entryFeeder.getByTs;
|
||||
const options = (searchString: string) =>
|
||||
new Promise<{ value: string, label: string | JSX.Element }[]>(resolve => {
|
||||
resolve(props.searchF(searchString).map(makeOption));
|
||||
resolve(search(searchString).map(makeOption));
|
||||
});
|
||||
const onChange = (v: { label: string | JSX.Element, value: string } | null) => {
|
||||
if (!v) {
|
||||
props.onChange(undefined);
|
||||
return;
|
||||
}
|
||||
const s = props.getByTs(parseInt(v.value));
|
||||
const s = getByTs(parseInt(v.value));
|
||||
if (!s) return;
|
||||
props.onChange(s);
|
||||
}
|
||||
|
@ -77,7 +75,8 @@ function EntrySelect<E extends T.DictionaryEntry | T.VerbEntry>(props: ({
|
|||
/>
|
||||
</div>;
|
||||
}
|
||||
const options = props.entries
|
||||
const entries = props.entryFeeder;
|
||||
const options = entries
|
||||
.sort((a, b) => {
|
||||
if ("entry" in a) {
|
||||
return a.entry.p.localeCompare("p" in b ? b.p : b.entry.p, "af-PS")
|
||||
|
@ -90,7 +89,7 @@ function EntrySelect<E extends T.DictionaryEntry | T.VerbEntry>(props: ({
|
|||
props.onChange(undefined);
|
||||
return;
|
||||
}
|
||||
const s = props.entries.find(e => (
|
||||
const s = entries.find(e => (
|
||||
("entry" in e)
|
||||
? e.entry.ts.toString() === v.value
|
||||
: e.ts.toString() === v.value
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
import * as T from "../../types";
|
||||
import { renderEP } from "../../lib/phrase-building/render-ep";
|
||||
import { compileEP } from "../../lib/phrase-building/compile-ep";
|
||||
import AbbreviationFormSelector from "../vp-explorer/AbbreviationFormSelector";
|
||||
import useStickyState from "../../lib/useStickyState";
|
||||
import Examples from "../Examples";
|
||||
|
||||
function EPDisplay({ eps, opts }: { eps: T.EPSelectionState, opts: T.TextOptions }) {
|
||||
const [form, setForm] = useStickyState<T.FormVersion>({ removeKing: false, shrinkServant: false }, "abbreviationFormEq");
|
||||
const EP = completeEPSelection(eps);
|
||||
if (!EP) {
|
||||
return <div className="lead text-center my-4">
|
||||
{(!eps.subject && !eps.predicate[eps.predicate.type])
|
||||
? "Select Subject and Predicate"
|
||||
: (eps.subject && !eps.predicate[eps.predicate.type])
|
||||
? "Select Predicate"
|
||||
: (!eps.subject && eps.predicate[eps.predicate.type])
|
||||
? "Select Subject"
|
||||
: ""}
|
||||
</div>
|
||||
}
|
||||
const rendered = renderEP(EP);
|
||||
const result = compileEP(rendered, form);
|
||||
return <div className="text-center pt-3">
|
||||
<AbbreviationFormSelector
|
||||
adjustable="king"
|
||||
form={{ ...form, shrinkServant: false }}
|
||||
onChange={setForm}
|
||||
/>
|
||||
{"long" in result.ps ?
|
||||
<div>
|
||||
<VariationLayer vs={result.ps.long} opts={opts} />
|
||||
<VariationLayer vs={result.ps.short} opts={opts} />
|
||||
{result.ps.mini && <VariationLayer vs={result.ps.mini} opts={opts} />}
|
||||
</div>
|
||||
: <VariationLayer vs={result.ps} opts={opts} />
|
||||
}
|
||||
{result.e && <div className="text-muted mt-3">
|
||||
{result.e.map((e, i) => <div key={i}>{e}</div>)}
|
||||
</div>}
|
||||
{EP.predicate.selection.type === "participle" && <div style={{ maxWidth: "6 00px", margin: "0 auto" }} className="alert alert-warning mt-3 pt-4">
|
||||
<p>⚠️ NOTE: This means that the subject {rendered.subject.e ? `(${rendered.subject.e})` : ""} is <strong>the action/idea</strong> of
|
||||
{` `}
|
||||
"{rendered.predicate.e ? rendered.predicate.e : "the particple"}".</p>
|
||||
<p>It <strong>does not</strong> mean that the subject is doing the action, which is what the transaltion sounds like in English.</p>
|
||||
</div>}
|
||||
</div>
|
||||
}
|
||||
|
||||
function VariationLayer({ vs, opts }: { vs: T.PsString[], opts: T.TextOptions }) {
|
||||
return <div className="mb-2">
|
||||
<Examples opts={opts} lineHeight={0}>{vs}</Examples>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function completeEPSelection(eps: T.EPSelectionState): T.EPSelectionComplete | undefined {
|
||||
if (!eps.subject) {
|
||||
return undefined;
|
||||
}
|
||||
if (eps.predicate.type === "Complement") {
|
||||
const selection = eps.predicate.Complement;
|
||||
if (!selection) return undefined;
|
||||
return {
|
||||
...eps,
|
||||
subject: eps.subject,
|
||||
predicate: {
|
||||
type: "Complement",
|
||||
selection,
|
||||
},
|
||||
};
|
||||
}
|
||||
// predicate is NP
|
||||
const selection = eps.predicate.NP;
|
||||
if (!selection) return undefined;
|
||||
return {
|
||||
...eps,
|
||||
subject: eps.subject,
|
||||
predicate: {
|
||||
type: "NP",
|
||||
selection,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default EPDisplay;
|
|
@ -0,0 +1,213 @@
|
|||
import * as T from "../../types";
|
||||
import useStickyState from "../../lib/useStickyState";
|
||||
import NPPicker from "../np-picker/NPPicker";
|
||||
import EquativePicker from "./EquativePicker";
|
||||
import EPDisplay from "./EPDisplay";
|
||||
import ButtonSelect from "../ButtonSelect";
|
||||
import EqCompPicker from "./eq-comp-picker/EqCompPicker";
|
||||
import { roleIcon } from "../vp-explorer/VPExplorerExplanationModal";
|
||||
import { isUnisexNounEntry } from "../../lib/type-predicates";
|
||||
import {
|
||||
personGender,
|
||||
personNumber,
|
||||
} from "../../lib/misc-helpers";
|
||||
import EqChartsDisplay from "./EqChartsDisplay";
|
||||
|
||||
// TODO: put the clear button beside the title in the predicate picker?
|
||||
|
||||
function EPExplorer(props: {
|
||||
opts: T.TextOptions,
|
||||
entryFeeder: T.EntryFeeder,
|
||||
}) {
|
||||
const [mode, setMode] = useStickyState<"charts" | "phrases">("charts", "EPExplorerMode");
|
||||
const [eps, setEps] = useStickyState<T.EPSelectionState>({
|
||||
subject: undefined,
|
||||
predicate: {
|
||||
type: "Complement",
|
||||
NP: undefined,
|
||||
Complement: undefined,
|
||||
},
|
||||
equative: {
|
||||
tense: "present",
|
||||
negative: false,
|
||||
},
|
||||
}, "EPSelectionState");
|
||||
function handlePredicateTypeChange(type: "NP" | "Complement") {
|
||||
setEps(o => ({
|
||||
...o,
|
||||
predicate: {
|
||||
...o.predicate,
|
||||
type,
|
||||
},
|
||||
}));
|
||||
}
|
||||
function handleSetSubject(subject: T.NPSelection | undefined) {
|
||||
setEps(old => massageSubjectChange(subject, old));
|
||||
}
|
||||
function setPredicateNP(selection: T.NPSelection | undefined) {
|
||||
setEps(o => massageNPPredicateChange(selection, o))
|
||||
}
|
||||
function setPredicateComp(selection: T.EqCompSelection | undefined) {
|
||||
setEps(o => ({
|
||||
...o,
|
||||
predicate: {
|
||||
...o.predicate,
|
||||
Complement: selection
|
||||
},
|
||||
}));
|
||||
}
|
||||
const king = eps.subject?.type === "pronoun"
|
||||
? "subject"
|
||||
: eps.predicate.type === "Complement"
|
||||
? "subject"
|
||||
: "predicate";
|
||||
return <div>
|
||||
<div className="mt-2 mb-3 text-center">
|
||||
<ButtonSelect
|
||||
value={mode}
|
||||
options={[
|
||||
{ label: "Charts", value: "charts" },
|
||||
{ label: "Phrases", value: "phrases" },
|
||||
]}
|
||||
handleChange={setMode}
|
||||
/>
|
||||
</div>
|
||||
<div className="d-flex flex-row justify-content-around flex-wrap" style={{ marginLeft: "-0.5rem", marginRight: "-0.5rem" }}>
|
||||
{mode === "phrases" && <>
|
||||
<div className="my-2">
|
||||
<NPPicker
|
||||
heading={<div className="h5 text-center">Subject {king === "subject" ? roleIcon.king : ""}</div>}
|
||||
entryFeeder={props.entryFeeder}
|
||||
np={eps.subject}
|
||||
counterPart={undefined}
|
||||
role="subject"
|
||||
onChange={handleSetSubject}
|
||||
opts={props.opts}
|
||||
/>
|
||||
</div>
|
||||
<div className="my-2">
|
||||
<div className="h5 text-center">Predicate {king === "predicate" ? roleIcon.king : ""}</div>
|
||||
<div className="mb-2 text-center">
|
||||
<ButtonSelect
|
||||
small
|
||||
options={[
|
||||
{ value: "NP", label: "NP" },
|
||||
{ value: "Complement", label: "Complement" },
|
||||
]}
|
||||
value={eps.predicate.type}
|
||||
handleChange={handlePredicateTypeChange}
|
||||
/>
|
||||
</div>
|
||||
{eps.predicate.type === "NP" ? <NPPicker
|
||||
entryFeeder={props.entryFeeder}
|
||||
np={eps.predicate.type === "NP" ? eps.predicate.NP : undefined}
|
||||
counterPart={undefined}
|
||||
role="subject"
|
||||
onChange={setPredicateNP}
|
||||
opts={props.opts}
|
||||
/> : <EqCompPicker
|
||||
comp={eps.predicate.type === "Complement" ? eps.predicate.Complement : undefined}
|
||||
onChange={setPredicateComp}
|
||||
opts={props.opts}
|
||||
entryFeeder={props.entryFeeder}
|
||||
/>}
|
||||
</div>
|
||||
</>}
|
||||
<div className="my-2">
|
||||
<div className="h5 text-center clickable">Equative</div>
|
||||
<EquativePicker
|
||||
equative={eps.equative}
|
||||
onChange={(equative) => setEps(o => ({ ...o, equative }))}
|
||||
hideNegative={mode === "charts"}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{mode === "charts" && <EqChartsDisplay tense={eps.equative.tense} opts={props.opts} />}
|
||||
{mode === "phrases" && <EPDisplay opts={props.opts} eps={eps} />}
|
||||
</div>;
|
||||
}
|
||||
|
||||
export default EPExplorer;
|
||||
|
||||
function massageNPPredicateChange(selection: T.NPSelection | undefined, old: T.EPSelectionState): T.EPSelectionState {
|
||||
if (!selection) {
|
||||
return {
|
||||
...old,
|
||||
predicate: {
|
||||
...old.predicate,
|
||||
NP: selection,
|
||||
},
|
||||
};
|
||||
}
|
||||
if (old.subject?.type === "pronoun" && selection.type === "noun" && isUnisexNounEntry(selection.entry)) {
|
||||
const { gender, number } = selection;
|
||||
const pronoun = old.subject.person;
|
||||
const newPronoun = movePersonNumber(movePersonGender(pronoun, gender), number);
|
||||
return {
|
||||
...old,
|
||||
subject: {
|
||||
...old.subject,
|
||||
person: newPronoun,
|
||||
},
|
||||
predicate: {
|
||||
...old.predicate,
|
||||
NP: selection,
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
...old,
|
||||
predicate: {
|
||||
...old.predicate,
|
||||
NP: selection,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function massageSubjectChange(subject: T.NPSelection | undefined, old: T.EPSelectionState): T.EPSelectionState {
|
||||
if (!subject) {
|
||||
return {
|
||||
...old,
|
||||
subject,
|
||||
};
|
||||
}
|
||||
if (subject.type === "pronoun" && old.predicate.type === "NP" && old.predicate.NP?.type === "noun" && isUnisexNounEntry(old.predicate.NP.entry)) {
|
||||
const predicate = old.predicate.NP;
|
||||
const numberAdjusted = predicate.changeNumber
|
||||
? predicate.changeNumber(personNumber(subject.person))
|
||||
: predicate;
|
||||
const fullyAdjusted = numberAdjusted.changeGender
|
||||
? numberAdjusted.changeGender(personGender(subject.person))
|
||||
: numberAdjusted;
|
||||
return {
|
||||
...old,
|
||||
subject,
|
||||
predicate: {
|
||||
...old.predicate,
|
||||
NP: fullyAdjusted,
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
...old,
|
||||
subject,
|
||||
};
|
||||
}
|
||||
|
||||
function movePersonGender(p: T.Person, gender: T.Gender): T.Person {
|
||||
const pGender = personGender(p);
|
||||
if (gender === pGender) {
|
||||
return p;
|
||||
}
|
||||
return (gender === "masc") ? (p - 1) : (p + 1);
|
||||
}
|
||||
|
||||
function movePersonNumber(p: T.Person, number: T.NounNumber): T.Person {
|
||||
const pNumber = personNumber(p);
|
||||
if (pNumber === number) {
|
||||
return p;
|
||||
}
|
||||
return (number === "plural")
|
||||
? (p + 6)
|
||||
: (p - 6);
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import * as T from "../../types";
|
||||
import VerbFormDisplay from "../VerbFormDisplay";
|
||||
import { baParticle } from "../../lib/grammar-units";
|
||||
import { getEquativeForm } from "../../lib/phrase-building/render-ep";
|
||||
import { addToForm } from "../../lib/p-text-helpers";
|
||||
|
||||
function EqChartsDisplay({ tense, opts }: {
|
||||
tense: T.EquativeTense,
|
||||
opts: T.TextOptions,
|
||||
}) {
|
||||
const { form, hasBa } = getEquativeForm(tense);
|
||||
const withBa = hasBa
|
||||
? addToForm([baParticle, " ", { p: "…", f: "…" }, " "], form)
|
||||
: form;
|
||||
return <div className="mb-4">
|
||||
<VerbFormDisplay
|
||||
displayForm={withBa}
|
||||
showingFormInfo={false}
|
||||
textOptions={opts}
|
||||
/>
|
||||
</div>
|
||||
};
|
||||
|
||||
export default EqChartsDisplay;
|
|
@ -0,0 +1,107 @@
|
|||
import * as T from "../../types"
|
||||
import Select from "react-select";
|
||||
import ButtonSelect from "../ButtonSelect";
|
||||
|
||||
export const zIndexProps = {
|
||||
menuPortalTarget: document.body,
|
||||
styles: { menuPortal: (base: any) => ({ ...base, zIndex: 9999 }) },
|
||||
};
|
||||
|
||||
const options: { label: string | JSX.Element, value: T.EquativeTense }[] = [{
|
||||
label: "Present",
|
||||
value: "present",
|
||||
}, {
|
||||
label: "Habitual",
|
||||
value: "habitual",
|
||||
}, {
|
||||
label: "Subjunctive",
|
||||
value: "subjunctive",
|
||||
}, {
|
||||
label: "Future",
|
||||
value: "future",
|
||||
}, {
|
||||
label: "Past",
|
||||
value: "past",
|
||||
}, {
|
||||
label: `"Would Be"`,
|
||||
value: "wouldBe",
|
||||
}, {
|
||||
label: "Past Subjunctive",
|
||||
value: "pastSubjunctive",
|
||||
}];
|
||||
|
||||
function EquativePicker({ equative, onChange, hideNegative }: {
|
||||
equative: { tense: T.EquativeTense, negative: boolean },
|
||||
onChange: (e: { tense: T.EquativeTense, negative: boolean }) => void,
|
||||
hideNegative?: boolean,
|
||||
}) {
|
||||
function onTenseSelect(o: { value: T.EquativeTense } | null) {
|
||||
const value = o?.value ? o.value : undefined;
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
onChange({
|
||||
...equative,
|
||||
tense: value,
|
||||
});
|
||||
}
|
||||
function moveTense(dir: "forward" | "back") {
|
||||
return () => {
|
||||
const currIndex = options.findIndex(tn => tn.value === equative.tense)
|
||||
if (currIndex === -1) {
|
||||
console.error("error moving tense", dir);
|
||||
return;
|
||||
}
|
||||
const newIndex = dir === "forward"
|
||||
? ((currIndex + 1) % options.length)
|
||||
: (currIndex === 0 ? (options.length - 1) : (currIndex - 1))
|
||||
const newTense = options[newIndex];
|
||||
onChange({
|
||||
...equative,
|
||||
tense: newTense.value,
|
||||
});
|
||||
};
|
||||
}
|
||||
function onPosNegSelect(value: "true" | "false") {
|
||||
onChange({
|
||||
...equative,
|
||||
negative: value === "true",
|
||||
});
|
||||
}
|
||||
return <div>
|
||||
<div style={{ maxWidth: "300px", minWidth: "250px", margin: "0 auto" }}>
|
||||
<div className="h5">Tense:</div>
|
||||
<Select
|
||||
isSearchable={false}
|
||||
// for some reason can't use tOptions with find here;
|
||||
value={options.find(o => o.value === equative.tense)}
|
||||
onChange={onTenseSelect}
|
||||
className="mb-2"
|
||||
options={options}
|
||||
{...zIndexProps}
|
||||
/>
|
||||
{<div className="d-flex flex-row justify-content-between align-items-center mt-3 mb-1" style={{ width: "100%" }}>
|
||||
<div className="btn btn-light clickable" onClick={moveTense("back")}>
|
||||
<i className="fas fa-chevron-left" />
|
||||
</div>
|
||||
{!hideNegative && <ButtonSelect
|
||||
small
|
||||
value={equative.negative.toString() as "true" | "false"}
|
||||
options={[{
|
||||
label: "Pos.",
|
||||
value: "false",
|
||||
}, {
|
||||
label: "Neg.",
|
||||
value: "true",
|
||||
}]}
|
||||
handleChange={onPosNegSelect}
|
||||
/>}
|
||||
<div onClick={moveTense("forward")} className="btn btn-light clickable">
|
||||
<i className="fas fa-chevron-right" />
|
||||
</div>
|
||||
</div>}
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
export default EquativePicker;
|
|
@ -0,0 +1,77 @@
|
|||
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."];
|
||||
|
||||
function EqCompPicker(props: {
|
||||
onChange: (comp: T.EqCompSelection | undefined) => void,
|
||||
comp: T.EqCompSelection | undefined,
|
||||
opts: T.TextOptions,
|
||||
cantClear?: boolean,
|
||||
heading?: JSX.Element | string,
|
||||
entryFeeder: T.EntryFeeder,
|
||||
}) {
|
||||
const [compType, setCompType] = useState<T.EqCompType | undefined>(props.comp ? props.comp.type : undefined);
|
||||
useEffect(() => {
|
||||
setCompType(props.comp ? props.comp.type : undefined);
|
||||
}, [props.comp]);
|
||||
function handleClear() {
|
||||
setCompType(undefined);
|
||||
props.onChange(undefined);
|
||||
}
|
||||
function handleCompTypeChange(ctp: T.EqCompType) {
|
||||
props.onChange(undefined);
|
||||
setCompType(ctp);
|
||||
}
|
||||
const clearButton = (compType && !props.cantClear)
|
||||
? <button className="btn btn-sm btn-light mb-2" onClick={handleClear}>X</button>
|
||||
: <div></div>;
|
||||
return <>
|
||||
<div className="d-flex flex-row justify-content-between">
|
||||
<div></div>
|
||||
<div>
|
||||
{typeof props.heading === "string"
|
||||
? <div className="h5 text-center">{props.heading}</div>
|
||||
: props.heading}
|
||||
</div>
|
||||
<div>
|
||||
{clearButton}
|
||||
</div>
|
||||
</div>
|
||||
{!compType && <div className="text-center">
|
||||
<div className="h6 mr-3">
|
||||
Choose Complement
|
||||
</div>
|
||||
{compTypes.map((cpt) => <div key={cpt} className="mb-2">
|
||||
<button
|
||||
key={cpt}
|
||||
type="button"
|
||||
className="mr-2 btn btn-sm btn-outline-secondary"
|
||||
onClick={() => handleCompTypeChange(cpt)}
|
||||
>
|
||||
{cpt}
|
||||
</button>
|
||||
</div>)}
|
||||
</div>}
|
||||
<div style={{ minWidth: "9rem" }}>
|
||||
{compType === "adjective" ?
|
||||
<AdjectivePicker
|
||||
entryFeeder={props.entryFeeder.adjectives}
|
||||
adjective={props.comp?.type === "adjective" ? props.comp : undefined}
|
||||
opts={props.opts}
|
||||
onChange={props.onChange}
|
||||
/>
|
||||
: compType === "loc. adv."
|
||||
? <LocativeAdverbPicker
|
||||
entryFeeder={props.entryFeeder.locativeAdverbs}
|
||||
adjective={props.comp?.type === "loc. adv." ? props.comp : undefined}
|
||||
opts={props.opts}
|
||||
onChange={props.onChange}
|
||||
/>
|
||||
: null}
|
||||
</div>
|
||||
</>;
|
||||
}
|
||||
|
||||
export default EqCompPicker;
|
|
@ -0,0 +1,37 @@
|
|||
import * as T from "../../../types";
|
||||
import EntrySelect from "../../EntrySelect";
|
||||
|
||||
function LocativeAdverbPicker(props: {
|
||||
entryFeeder: T.EntryFeederSingleType<T.LocativeAdverbEntry>,
|
||||
adjective: T.LocativeAdverbSelection | undefined,
|
||||
onChange: (p: T.LocativeAdverbSelection | undefined) => void,
|
||||
opts: T.TextOptions,
|
||||
}) {
|
||||
function onEntrySelect(entry: T.LocativeAdverbEntry | undefined) {
|
||||
if (!entry) {
|
||||
return props.onChange(undefined);
|
||||
}
|
||||
props.onChange(makeLocativeAdverbSelection(entry));
|
||||
}
|
||||
return <div style={{ maxWidth: "225px", minWidth: "125px" }}>
|
||||
<h6>Loc. Adverb</h6>
|
||||
<div>
|
||||
<EntrySelect
|
||||
value={props.adjective?.entry}
|
||||
entryFeeder={props.entryFeeder}
|
||||
onChange={onEntrySelect}
|
||||
name="Locative Adverb"
|
||||
opts={props.opts}
|
||||
/>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
export default LocativeAdverbPicker;
|
||||
|
||||
function makeLocativeAdverbSelection(entry: T.LocativeAdverbEntry): T.LocativeAdverbSelection {
|
||||
return {
|
||||
type: "loc. adv.",
|
||||
entry: entry,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
import * as T from "../../types";
|
||||
import EntrySelect from "../EntrySelect";
|
||||
|
||||
function AdjectivePicker(props: {
|
||||
entryFeeder: T.EntryFeederSingleType<T.AdjectiveEntry>,
|
||||
adjective: T.AdjectiveSelection | undefined,
|
||||
onChange: (p: T.AdjectiveSelection | undefined) => void,
|
||||
opts: T.TextOptions,
|
||||
}) {
|
||||
function onEntrySelect(entry: T.AdjectiveEntry | undefined) {
|
||||
if (!entry) {
|
||||
return props.onChange(undefined);
|
||||
}
|
||||
props.onChange(makeAdjectiveSelection(entry));
|
||||
}
|
||||
return <div style={{ maxWidth: "225px", minWidth: "125px" }}>
|
||||
<h6>Adjective</h6>
|
||||
<div>
|
||||
<EntrySelect
|
||||
value={props.adjective?.entry}
|
||||
entryFeeder={props.entryFeeder}
|
||||
onChange={onEntrySelect}
|
||||
name="Adjective"
|
||||
opts={props.opts}
|
||||
/>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function makeAdjectiveSelection(entry: T.AdjectiveEntry): T.AdjectiveSelection {
|
||||
return {
|
||||
type: "adjective",
|
||||
entry: entry,
|
||||
};
|
||||
}
|
||||
|
||||
export default AdjectivePicker;
|
|
@ -55,12 +55,8 @@ import EntrySelect from "../EntrySelect";
|
|||
// : () => true;
|
||||
// }
|
||||
|
||||
function NPNounPicker(props: ({
|
||||
nouns: T.NounEntry[],
|
||||
} | {
|
||||
nouns: (s: string) => T.NounEntry[],
|
||||
getNounByTs: (ts: number) => T.NounEntry | undefined;
|
||||
}) & {
|
||||
function NPNounPicker(props: {
|
||||
entryFeeder: T.EntryFeederSingleType<T.NounEntry>,
|
||||
noun: T.NounSelection | undefined,
|
||||
onChange: (p: T.NounSelection | undefined) => void,
|
||||
opts: T.TextOptions,
|
||||
|
@ -98,12 +94,7 @@ function NPNounPicker(props: ({
|
|||
{!(props.noun && props.noun.dynamicComplement) ? <div>
|
||||
<EntrySelect
|
||||
value={props.noun?.entry}
|
||||
{..."getNounByTs" in props ? {
|
||||
getByTs: props.getNounByTs,
|
||||
searchF: props.nouns
|
||||
} : {
|
||||
entries: props.nouns,
|
||||
}}
|
||||
entryFeeder={props.entryFeeder}
|
||||
onChange={onEntrySelect}
|
||||
name="Noun"
|
||||
opts={props.opts}
|
||||
|
|
|
@ -8,12 +8,8 @@ function makeParticipleSelection(verb: T.VerbEntry): T.ParticipleSelection {
|
|||
};
|
||||
}
|
||||
|
||||
function NPParticiplePicker(props: ({
|
||||
verbs: T.VerbEntry[],
|
||||
} | {
|
||||
verbs: (s: string) => T.VerbEntry[],
|
||||
getVerbByTs: (ts: number) => T.VerbEntry | undefined;
|
||||
}) & {
|
||||
function NPParticiplePicker(props: {
|
||||
entryFeeder: T.EntryFeederSingleType<T.VerbEntry>,
|
||||
participle: T.ParticipleSelection | undefined,
|
||||
onChange: (p: T.ParticipleSelection | undefined) => void,
|
||||
opts: T.TextOptions,
|
||||
|
@ -29,12 +25,7 @@ function NPParticiplePicker(props: ({
|
|||
<h6>Participle</h6>
|
||||
<EntrySelect
|
||||
value={props.participle?.verb}
|
||||
{..."getVerbByTs" in props ? {
|
||||
getByTs: props.getVerbByTs,
|
||||
searchF: props.verbs,
|
||||
} : {
|
||||
entries: props.verbs,
|
||||
}}
|
||||
entryFeeder={props.entryFeeder}
|
||||
onChange={onEntrySelect}
|
||||
name="Pariticple"
|
||||
opts={props.opts}
|
||||
|
|
|
@ -14,7 +14,7 @@ import { isSecondPerson } from "../../lib/phrase-building/vp-tools";
|
|||
const npTypes: T.NPType[] = ["pronoun", "noun", "participle"];
|
||||
|
||||
function NPPicker(props: {
|
||||
heading?: JSX.Element,
|
||||
heading?: JSX.Element | string,
|
||||
onChange: (nps: T.NPSelection | undefined) => void,
|
||||
np: T.NPSelection | undefined,
|
||||
counterPart: T.NPSelection | T.VerbObject | undefined,
|
||||
|
@ -22,15 +22,8 @@ function NPPicker(props: {
|
|||
opts: T.TextOptions,
|
||||
cantClear?: boolean,
|
||||
is2ndPersonPicker?: boolean,
|
||||
} & ({
|
||||
nouns: (s: string) => T.NounEntry[],
|
||||
verbs: (s: string) => T.VerbEntry[],
|
||||
getNounByTs: (ts: number) => T.NounEntry | undefined,
|
||||
getVerbByTs: (ts: number) => T.VerbEntry | undefined,
|
||||
} | {
|
||||
nouns: T.NounEntry[],
|
||||
verbs: T.VerbEntry[],
|
||||
})) {
|
||||
entryFeeder: T.EntryFeeder,
|
||||
}) {
|
||||
if (props.is2ndPersonPicker && ((props.np?.type !== "pronoun") || !isSecondPerson(props.np.person))) {
|
||||
throw new Error("can't use 2ndPerson NPPicker without a pronoun");
|
||||
}
|
||||
|
@ -66,7 +59,9 @@ function NPPicker(props: {
|
|||
<div className="d-flex flex-row justify-content-between">
|
||||
<div></div>
|
||||
<div>
|
||||
{props.heading}
|
||||
{typeof props.heading === "string"
|
||||
? <div className="h5 text-center">{props.heading}</div>
|
||||
: props.heading}
|
||||
</div>
|
||||
<div>
|
||||
{npType && clearButton}
|
||||
|
@ -77,7 +72,7 @@ function NPPicker(props: {
|
|||
<div className="h6 mr-3">
|
||||
Choose NP
|
||||
</div>
|
||||
{npTypes.map((npt) => <div className="mb-2">
|
||||
{npTypes.map((npt) => <div key={npt} className="mb-2">
|
||||
<button
|
||||
key={npt}
|
||||
type="button"
|
||||
|
@ -98,24 +93,14 @@ function NPPicker(props: {
|
|||
/>
|
||||
: npType === "noun"
|
||||
? <NounPicker
|
||||
{..."getNounByTs" in props ? {
|
||||
nouns: props.nouns,
|
||||
getNounByTs: props.getNounByTs,
|
||||
} : {
|
||||
nouns: props.nouns,
|
||||
}}
|
||||
entryFeeder={props.entryFeeder.nouns}
|
||||
noun={(props.np && props.np.type === "noun") ? props.np : undefined}
|
||||
onChange={props.onChange}
|
||||
opts={props.opts}
|
||||
/>
|
||||
: npType === "participle"
|
||||
? <ParticiplePicker
|
||||
{..."getVerbByTs" in props ? {
|
||||
verbs: props.verbs,
|
||||
getVerbByTs: props.getVerbByTs,
|
||||
} : {
|
||||
verbs: props.verbs,
|
||||
}}
|
||||
entryFeeder={props.entryFeeder.verbs}
|
||||
participle={(props.np && props.np.type === "participle") ? props.np : undefined}
|
||||
onChange={props.onChange}
|
||||
opts={props.opts}
|
||||
|
|
|
@ -141,13 +141,14 @@ function NPPronounPicker({ onChange, pronoun, role, clearButton, opts, is2ndPers
|
|||
<table className="table table-bordered table-sm" style={{ textAlign: "center", minWidth: "100px", tableLayout: "fixed" }}>
|
||||
<tbody>
|
||||
{pSpec.map((rw, i) => (
|
||||
<tr>
|
||||
<tr key={`pronounPickerRow${i}`}>
|
||||
{rw.map((r, j) => {
|
||||
const active = is2ndPersonPicker
|
||||
? (p.col === j)
|
||||
: (p.row === i && p.col === j);
|
||||
const content = typeof r === "string" ? r : r[p.gender];
|
||||
return <td
|
||||
key={`pronounPickerCell${i}${j}`}
|
||||
onClick={() => {
|
||||
handleClick(is2ndPersonPicker ? 1 : i, j);
|
||||
}}
|
||||
|
|
|
@ -8,11 +8,11 @@ const options = [
|
|||
value: "full",
|
||||
},
|
||||
{
|
||||
label: <div>Kill {roleIcon.king}</div>,
|
||||
label: <div className="m1-2">No {roleIcon.king}</div>,
|
||||
value: "noKing",
|
||||
},
|
||||
{
|
||||
label: <div>Shrink {roleIcon.servant}</div>,
|
||||
label: <div>Mini {roleIcon.servant}</div>,
|
||||
value: "shrinkServant",
|
||||
},
|
||||
{
|
||||
|
|
|
@ -109,11 +109,11 @@ export function getRandomTense(o?: T.PerfectTense | T.VerbTense | T.ModalTense |
|
|||
}
|
||||
|
||||
function TensePicker(props: ({
|
||||
vps: T.VPSelection,
|
||||
vps: T.VPSelectionState,
|
||||
} | {
|
||||
vpsComplete: T.VPSelectionComplete,
|
||||
}) & {
|
||||
onChange: (p: T.VPSelection) => void,
|
||||
onChange: (p: T.VPSelectionState) => void,
|
||||
mode: "charts" | "phrases" | "quiz",
|
||||
}) {
|
||||
const [showFormula, setShowFormula] = useStickyState<boolean>(false, "showFormula");
|
||||
|
@ -228,7 +228,7 @@ function TensePicker(props: ({
|
|||
return <div>
|
||||
<div style={{ maxWidth: "300px", minWidth: "250px", margin: "0 auto" }}>
|
||||
<div className="d-flex flex-row justify-content-between align-items-center">
|
||||
<div className="h5">Tense:</div>
|
||||
<div className="h5">Verb Tense:</div>
|
||||
{canHaveFormula && <div className="clickable mb-2 small" onClick={() => setShowFormula(x => !x)}>
|
||||
🧪 {!showFormula ? "Show" : "Hide"} Formula
|
||||
</div>}
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
import useStickyState from "../../lib/useStickyState";
|
||||
import Examples from "../Examples";
|
||||
|
||||
function VPDisplay({ VP, opts }: { VP: T.VPSelection, opts: T.TextOptions }) {
|
||||
function VPDisplay({ VP, opts }: { VP: T.VPSelectionState, opts: T.TextOptions }) {
|
||||
const [form, setForm] = useStickyState<T.FormVersion>({ removeKing: false, shrinkServant: false }, "abbreviationForm");
|
||||
const [OSV, setOSV] = useStickyState<boolean>(false, "includeOSV");
|
||||
const VPComplete = completeVPSelection(VP);
|
||||
|
@ -21,7 +21,7 @@ function VPDisplay({ VP, opts }: { VP: T.VPSelection, opts: T.TextOptions }) {
|
|||
</div>;
|
||||
}
|
||||
const result = compileVP(renderVP(VPComplete), { ...form, OSV });
|
||||
return <div className="text-center">
|
||||
return <div className="text-center mt-1">
|
||||
{VP.verb.transitivity === "transitive" && <div className="form-check mb-2">
|
||||
<input
|
||||
className="form-check-input"
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
isInvalidSubjObjCombo,
|
||||
} from "../../lib/phrase-building/vp-tools";
|
||||
import * as T from "../../types";
|
||||
import ChartDisplay from "./ChartDisplay";
|
||||
import ChartDisplay from "./VPChartDisplay";
|
||||
import useStickyState from "../../lib/useStickyState";
|
||||
import { makeVPSelectionState } from "./verb-selection";
|
||||
import { useEffect, useState } from "react";
|
||||
|
@ -41,16 +41,9 @@ export function VPExplorer(props: {
|
|||
verb: T.VerbEntry,
|
||||
opts: T.TextOptions,
|
||||
handleLinkClick: ((ts: number) => void) | "none",
|
||||
} & ({
|
||||
nouns: T.NounEntry[],
|
||||
verbs: T.VerbEntry[],
|
||||
} | {
|
||||
nouns: (s: string) => T.NounEntry[],
|
||||
verbs: (s: string) => T.VerbEntry[],
|
||||
getNounByTs: (ts: number) => T.NounEntry | undefined,
|
||||
getVerbByTs: (ts: number) => T.VerbEntry | undefined,
|
||||
})) {
|
||||
const [vps, setVps] = useStickyState<T.VPSelection>(
|
||||
entryFeeder: T.EntryFeeder,
|
||||
}) {
|
||||
const [vps, setVps] = useStickyState<T.VPSelectionState>(
|
||||
savedVps => makeVPSelectionState(props.verb, savedVps),
|
||||
"vpsState5",
|
||||
);
|
||||
|
@ -115,12 +108,6 @@ export function VPExplorer(props: {
|
|||
}
|
||||
return <div className="mt-3" style={{ maxWidth: "950px"}}>
|
||||
<VerbPicker
|
||||
{..."getNounByTs" in props ? {
|
||||
getVerbByTs: props.getVerbByTs,
|
||||
verbs: props.verbs,
|
||||
} : {
|
||||
verbs: props.verbs,
|
||||
}}
|
||||
vps={vps}
|
||||
onChange={quizLock(setVps)}
|
||||
opts={props.opts}
|
||||
|
@ -150,15 +137,7 @@ export function VPExplorer(props: {
|
|||
heading={roles.king === "subject"
|
||||
? <div className="h5 text-center clickable" onClick={() => setShowingExplanation({ role: "king", item: "subject" })}>Subject {roleIcon.king}</div>
|
||||
: <div className="h5 text-center clickable" onClick={() => setShowingExplanation({ role: "servant", item: "subject" })}>Subject {roleIcon.servant}</div>}
|
||||
{..."getNounByTs" in props ? {
|
||||
getNounByTs: props.getNounByTs,
|
||||
getVerbByTs: props.getVerbByTs,
|
||||
nouns: props.nouns,
|
||||
verbs: props.verbs,
|
||||
} : {
|
||||
nouns: props.nouns,
|
||||
verbs: props.verbs,
|
||||
}}
|
||||
entryFeeder={props.entryFeeder}
|
||||
role={(isPast && vps.verb.transitivity !== "intransitive")
|
||||
? "ergative"
|
||||
: "subject"
|
||||
|
@ -177,15 +156,7 @@ export function VPExplorer(props: {
|
|||
heading={roles.king === "object"
|
||||
? <div className="h5 text-center clickable" onClick={() => setShowingExplanation({ role: "king", item: "object" })}>Object {roleIcon.king}</div>
|
||||
: <div className="h5 text-center clickable" onClick={() => setShowingExplanation({ role: "servant", item: "object" })}>Object {roleIcon.servant}</div>}
|
||||
{..."getNounByTs" in props ? {
|
||||
getNounByTs: props.getNounByTs,
|
||||
getVerbByTs: props.getVerbByTs,
|
||||
nouns: props.nouns,
|
||||
verbs: props.verbs,
|
||||
} : {
|
||||
nouns: props.nouns,
|
||||
verbs: props.verbs,
|
||||
}}
|
||||
entryFeeder={props.entryFeeder}
|
||||
role="object"
|
||||
np={vps.verb.object}
|
||||
counterPart={vps.subject}
|
||||
|
|
|
@ -14,7 +14,7 @@ import playAudio from "../../lib/play-audio";
|
|||
import TensePicker from "./TensePicker";
|
||||
import Keyframes from "../Keyframes";
|
||||
import energyDrink from "./energy-drink.jpg";
|
||||
import { flattenLengths } from "../../lib/phrase-building/compile-vp";
|
||||
import { flattenLengths } from "../../lib/phrase-building/segment";
|
||||
import { concatPsString } from "../../lib/p-text-helpers";
|
||||
import { isImperativeTense } from "../../lib/type-predicates";
|
||||
|
||||
|
@ -56,7 +56,7 @@ type MixType = "NPs" | "tenses" | "both";
|
|||
|
||||
function VPExplorerQuiz(props: {
|
||||
opts: T.TextOptions,
|
||||
vps: T.VPSelection,
|
||||
vps: T.VPSelectionState,
|
||||
}) {
|
||||
const startingQs = tickQuizState(completeVPs(props.vps));
|
||||
const [quizState, setQuizState] = useState<QuizState>(startingQs);
|
||||
|
@ -370,7 +370,7 @@ function getOptionFromResult(r: {
|
|||
return ps[0];
|
||||
}
|
||||
|
||||
function completeVPs(vps: T.VPSelection): T.VPSelectionComplete {
|
||||
function completeVPs(vps: T.VPSelectionState): T.VPSelectionComplete {
|
||||
const oldSubj = vps.subject?.type === "pronoun"
|
||||
? vps.subject.person
|
||||
: undefined;
|
||||
|
|
|
@ -9,8 +9,8 @@ import CompoundDisplay from "./CompoundDisplay";
|
|||
// TODO: dark on past tense selecitons
|
||||
|
||||
function VerbPicker(props: {
|
||||
vps: T.VPSelection,
|
||||
onChange: (p: T.VPSelection) => void,
|
||||
vps: T.VPSelectionState,
|
||||
onChange: (p: T.VPSelectionState) => void,
|
||||
opts: T.TextOptions,
|
||||
handleLinkClick: ((ts: number) => void) | "none",
|
||||
}) {
|
||||
|
|
|
@ -6,8 +6,8 @@ import { getVerbInfo } from "../../lib/verb-info";
|
|||
|
||||
export function makeVPSelectionState(
|
||||
verb: T.VerbEntry,
|
||||
os?: T.VPSelection,
|
||||
): T.VPSelection {
|
||||
os?: T.VPSelectionState,
|
||||
): T.VPSelectionState {
|
||||
const info = getVerbInfo(verb.entry, verb.complement);
|
||||
const subject = (os?.verb.voice === "passive" && info.type === "dynamic compound")
|
||||
? makeNounSelection(info.objComplement.entry as T.NounEntry, true)
|
||||
|
|
|
@ -58,7 +58,7 @@ export function accentPastParticiple(s: T.PsString): T.PsString {
|
|||
}
|
||||
|
||||
export function splitUpSyllables(f: string): string[] {
|
||||
return f.match(/ |([^a|e|i|o|u| ]*(aa|a|ey|ee|e|oo|o|i|u)[^a|e|i|o|u| ]*)/ig) || [] as string[];
|
||||
return f.match(/ |([^a|á|e|é|i|í|o|ó|u|ú| ]*(aa|áa|a|á|ey|éy|ee|ée|e|é|oo|óo|o|ó|i|í|u|ú)[^a|á|e|é|i|í|o|ó|u|ú| ]*)/ig) || [] as string[];
|
||||
}
|
||||
|
||||
export function countSyllables(f: T.PsString | string): number {
|
||||
|
@ -110,7 +110,11 @@ function accentSyllable(s: string): string {
|
|||
|
||||
export function removeAccents(s: T.PsString): T.PsString;
|
||||
export function removeAccents(s: string): string;
|
||||
export function removeAccents(s: T.PsString | string): T.PsString | string {
|
||||
export function removeAccents(s: T.PsString[]): T.PsString[];
|
||||
export function removeAccents(s: T.PsString | string | T.PsString[]): T.PsString | string | T.PsString[] {
|
||||
if (Array.isArray(s)) {
|
||||
return s.map(t => removeAccents(t));
|
||||
}
|
||||
if (typeof s !== "string") {
|
||||
return {
|
||||
...s,
|
||||
|
|
|
@ -81,7 +81,7 @@ export function chooseParticipleInflection(
|
|||
return pPartInfs; // already just one thing
|
||||
}
|
||||
|
||||
export function getPersonNumber(gender: "masc" | "fem", number: "singular" | "plural"): T.Person {
|
||||
export function getPersonNumber(gender: T.Gender, number: T.NounNumber): T.Person {
|
||||
const base = gender === "masc" ? 4 : 5;
|
||||
return base + (number === "singular" ? 0 : 6);
|
||||
}
|
||||
|
@ -129,10 +129,14 @@ export function getAuxTransitivity(trans: T.Transitivity): "transitive" | "intra
|
|||
return trans === "intransitive" ? "intransitive" : "transitive";
|
||||
}
|
||||
|
||||
export function personGender(person: T.Person): "masc" | "fem" {
|
||||
export function personGender(person: T.Person): T.Gender {
|
||||
return person % 2 === 0 ? "masc" : "fem";
|
||||
}
|
||||
|
||||
export function personNumber(person: T.Person): T.NounNumber {
|
||||
return personIsPlural(person) ? "plural" : "singular";
|
||||
}
|
||||
|
||||
export function personIsPlural(person: T.Person): boolean {
|
||||
return person > 5;
|
||||
}
|
||||
|
|
|
@ -126,6 +126,24 @@ const adjectives: Array<{
|
|||
},
|
||||
},
|
||||
},
|
||||
// regular adjective ending in a consonant with an accent already
|
||||
{
|
||||
in: {"ts":1527818704,"i":352,"p":"ارت","f":"arát","g":"arat","e":"wide, spacious, extensive","c":"adj."},
|
||||
out: {
|
||||
inflections: {
|
||||
masc: [
|
||||
[{p: "ارت", f: "arát"}],
|
||||
[{p: "ارت", f: "arát"}],
|
||||
[{p: "ارتو", f: "aráto"}],
|
||||
],
|
||||
fem: [
|
||||
[{p: "ارته", f: "aráta"}],
|
||||
[{p: "ارتې", f: "aráte"}],
|
||||
[{p: "ارتو", f: "aráto"}],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
in: {
|
||||
ts: 1527812862,
|
||||
|
|
|
@ -49,7 +49,7 @@ export function inflectWord(word: T.DictionaryEntry): T.InflectorOutput {
|
|||
};
|
||||
}
|
||||
if (w.c && w.c.includes("pl.")) {
|
||||
return handlePluralNoun(w);
|
||||
return handlePluralNounOrAdj(w);
|
||||
}
|
||||
if (w.c && (w.c.includes("adj.") || w.c.includes("unisex") || w.c.includes("num"))) {
|
||||
return handleUnisexWord(w);
|
||||
|
@ -104,7 +104,7 @@ function handleUnisexWord(word: T.DictionaryEntryNoFVars): T.InflectorOutput {
|
|||
return false;
|
||||
}
|
||||
|
||||
function handlePluralNoun(w: T.DictionaryEntryNoFVars): T.InflectorOutput {
|
||||
function handlePluralNounOrAdj(w: T.DictionaryEntryNoFVars): T.InflectorOutput {
|
||||
if (!w.c || !w.c.includes("n.")) return false;
|
||||
const plurals = makePlural(w);
|
||||
if (w.noInf) {
|
||||
|
@ -261,7 +261,7 @@ function inflectEmphasizedYeyUnisex(p: string, f: string): T.UnisexInflections {
|
|||
}
|
||||
|
||||
function inflectConsonantEndingUnisex(p: string, f: string): T.UnisexInflections {
|
||||
const fSyls = splitUpSyllables(f);
|
||||
const fSyls = splitUpSyllables(removeAccents(f));
|
||||
const iBase = fSyls.length === 1
|
||||
? makePsString(p, accentFSylsOnNFromEnd(fSyls, 0))
|
||||
: makePsString(p, f);
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
import * as T from "../../types";
|
||||
import * as grammarUnits from "../grammar-units";
|
||||
import {
|
||||
removeDuplicates,
|
||||
} from "./vp-tools";
|
||||
import {
|
||||
combineSegments,
|
||||
makeSegment,
|
||||
putKidsInKidsSection,
|
||||
Segment,
|
||||
flattenLengths,
|
||||
} from "./segment";
|
||||
import { removeAccents } from "../accent-helpers";
|
||||
|
||||
export function compileEP(EP: T.EPRendered, form: T.FormVersion): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] };
|
||||
export function compileEP(EP: T.EPRendered, form: T.FormVersion, combineLengths: true): { ps: T.PsString[], e?: string [] };
|
||||
export function compileEP(EP: T.EPRendered, form: T.FormVersion, combineLengths?: true): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] } {
|
||||
const { kids, NPs } = getSegmentsAndKids(EP, form);
|
||||
const equative = EP.equative.ps;
|
||||
const psResult = compilePs({
|
||||
NPs,
|
||||
kids,
|
||||
equative,
|
||||
negative: EP.equative.negative,
|
||||
});
|
||||
return {
|
||||
ps: combineLengths ? flattenLengths(psResult) : psResult,
|
||||
e: compileEnglish(EP),
|
||||
};
|
||||
}
|
||||
|
||||
function getSegmentsAndKids(EP: T.EPRendered, form: T.FormVersion): { kids: Segment[], NPs: Segment[] } {
|
||||
function ifNotRemoved(s: Segment, role: "subject" | "predicate"): Segment[] {
|
||||
if (form.removeKing && EP.king === role) {
|
||||
return [];
|
||||
}
|
||||
return [s];
|
||||
}
|
||||
const subject = makeSegment(EP.subject.ps);
|
||||
const predicate = makeSegment(EP.predicate.ps);
|
||||
return {
|
||||
kids: EP.equative.hasBa
|
||||
? [makeSegment(grammarUnits.baParticle, ["isBa", "isKid"])]
|
||||
: [],
|
||||
NPs: [
|
||||
...ifNotRemoved(subject, "subject"),
|
||||
...ifNotRemoved(predicate, "predicate"),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function compilePs({ NPs, kids, equative, negative }: {
|
||||
NPs: Segment[],
|
||||
kids: Segment[],
|
||||
equative: T.SingleOrLengthOpts<T.PsString[]>,
|
||||
negative: boolean,
|
||||
}): T.SingleOrLengthOpts<T.PsString[]> {
|
||||
if ("long" in equative) {
|
||||
return {
|
||||
long: compilePs({ NPs, kids, equative: equative.long, negative }) as T.PsString[],
|
||||
short: compilePs({ NPs, kids, equative: equative.short, negative }) as T.PsString[],
|
||||
};
|
||||
}
|
||||
const allSegments = putKidsInKidsSection([
|
||||
...NPs,
|
||||
...negative ? [
|
||||
makeSegment({ p: "نه", f: "nú" }),
|
||||
makeSegment(removeAccents(equative))
|
||||
] : [
|
||||
makeSegment(equative),
|
||||
],
|
||||
], kids);
|
||||
return removeDuplicates(combineSegments(allSegments, "spaces"));
|
||||
}
|
||||
|
||||
function compileEnglish(EP: T.EPRendered): string[] | undefined {
|
||||
function insertEWords(e: string, { subject, predicate }: { subject: string, predicate: string }): string {
|
||||
return e.replace("$SUBJ", subject).replace("$PRED", predicate || "");
|
||||
}
|
||||
const engSubj = EP.subject.e || undefined;
|
||||
const engPred = EP.predicate.e || undefined;
|
||||
// require all English parts for making the English phrase
|
||||
return (EP.englishBase && engSubj && engPred)
|
||||
? EP.englishBase.map(e => insertEWords(e, {
|
||||
subject: engSubj,
|
||||
predicate: engPred,
|
||||
}))
|
||||
: undefined;
|
||||
}
|
|
@ -2,7 +2,14 @@ import * as T from "../../types";
|
|||
import {
|
||||
concatPsString,
|
||||
} from "../p-text-helpers";
|
||||
import { makePsString } from "../accent-and-ps-utils";
|
||||
import {
|
||||
Segment,
|
||||
makeSegment,
|
||||
flattenLengths,
|
||||
combineSegments,
|
||||
splitOffLeapfrogWord,
|
||||
putKidsInKidsSection,
|
||||
} from "./segment";
|
||||
import {
|
||||
removeAccents,
|
||||
} from "../accent-helpers";
|
||||
|
@ -116,19 +123,6 @@ function getSegmentsAndKids(VP: T.VPRendered, form: Form): { kids: Segment[], NP
|
|||
};
|
||||
}
|
||||
|
||||
function putKidsInKidsSection(segments: Segment[], kids: Segment[]): Segment[] {
|
||||
const first = segments[0];
|
||||
const rest = segments.slice(1);
|
||||
return [
|
||||
first,
|
||||
// TODO: simplify to just isKidAfterHead ??
|
||||
...(first.isVerbHead && rest[0] && rest[0].isVerbRest)
|
||||
? kids.map(k => k.adjust({ desc: ["isKidBetweenHeadAndRest"] }))
|
||||
: kids,
|
||||
...rest,
|
||||
];
|
||||
}
|
||||
|
||||
function arrangeVerbWNegative(head: T.PsString | undefined, restRaw: T.PsString[], V: T.VerbRendered): Segment[][] {
|
||||
const hasLeapfrog = isPerfectTense(V.tense) || isModalTense(V.tense);
|
||||
const rest = (() => {
|
||||
|
@ -299,91 +293,3 @@ function compileEnglish(VP: T.VPRendered): string[] | undefined {
|
|||
: undefined;
|
||||
}
|
||||
|
||||
// SEGMENT TOOLS
|
||||
// TODO: PULL OUT FOR SHARING ACROSS COMPILE EP ETC?
|
||||
|
||||
type SegmentDescriptions = {
|
||||
isVerbHead?: boolean,
|
||||
isOoOrWaaHead?: boolean,
|
||||
isVerbRest?: boolean,
|
||||
isMiniPronoun?: boolean,
|
||||
isKid?: boolean,
|
||||
// TODO: Simplify to just isKidAfterHead?
|
||||
isKidBetweenHeadAndRest?: boolean,
|
||||
isNu?: boolean,
|
||||
isBa?: boolean,
|
||||
}
|
||||
|
||||
type SDT = keyof SegmentDescriptions;
|
||||
type Segment = { ps: T.PsString[] } & SegmentDescriptions & {
|
||||
adjust: (o: { ps?: T.PsString | T.PsString[] | ((ps: T.PsString) => T.PsString), desc?: SDT[] }) => Segment,
|
||||
};
|
||||
|
||||
function makeSegment(
|
||||
ps: T.PsString | T.PsString[],
|
||||
options?: (keyof SegmentDescriptions)[],
|
||||
): Segment {
|
||||
return {
|
||||
ps: Array.isArray(ps) ? ps : [ps],
|
||||
...options && options.reduce((all, curr) => ({
|
||||
...all,
|
||||
[curr]: true,
|
||||
}), {}),
|
||||
adjust: function(o): Segment {
|
||||
return {
|
||||
...this,
|
||||
...o.ps ? {
|
||||
ps: Array.isArray(o.ps)
|
||||
? o.ps
|
||||
: "p" in o.ps
|
||||
? [o.ps]
|
||||
: this.ps.map(o.ps)
|
||||
} : {},
|
||||
...o.desc && o.desc.reduce((all, curr) => ({
|
||||
...all,
|
||||
[curr]: true,
|
||||
}), {}),
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function combineSegments(loe: (Segment | " " | "" | T.PsString)[]): T.PsString[] {
|
||||
const first = loe[0];
|
||||
const rest = loe.slice(1);
|
||||
if (!rest.length) {
|
||||
if (typeof first === "string" || !("ps" in first)) {
|
||||
throw new Error("can't end with a spacer");
|
||||
}
|
||||
return first.ps;
|
||||
}
|
||||
return combineSegments(rest).flatMap(r => (
|
||||
(typeof first === "object" && "ps" in first)
|
||||
? first.ps.map(f => concatPsString(f, r))
|
||||
: [concatPsString(first, r)]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function flattenLengths(r: T.SingleOrLengthOpts<T.PsString[]>): T.PsString[] {
|
||||
if ("long" in r) {
|
||||
return Object.values(r).flat();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
function splitOffLeapfrogWord(psVs: T.PsString[]): [T.PsString[], T.PsString[]] {
|
||||
return psVs.reduce((tp, ps) => {
|
||||
const pWords = ps.p.split(" ");
|
||||
const fWords = ps.f.split(" ");
|
||||
const beginning = makePsString(
|
||||
pWords.slice(0, -1).join(" "),
|
||||
fWords.slice(0, -1).join(" "),
|
||||
);
|
||||
const end = makePsString(
|
||||
pWords.slice(-1).join(" "),
|
||||
fWords.slice(-1).join(" "),
|
||||
);
|
||||
return [[...tp[0], beginning], [...tp[1], end]];
|
||||
}, [[], []] as [T.PsString[], T.PsString[]]);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
import * as T from "../../types";
|
||||
import * as g from "../grammar-units";
|
||||
import {
|
||||
getPersonFromNP,
|
||||
} from "./vp-tools";
|
||||
import { renderNPSelection } from "./render-np";
|
||||
import { getPersonFromVerbForm } from "../../lib/misc-helpers";
|
||||
import { getVerbBlockPosFromPerson } from "../misc-helpers";
|
||||
import { getEnglishWord } from "../get-english-word";
|
||||
import { isUnisexSet, psStringFromEntry } from "../p-text-helpers";
|
||||
import { inflectWord } from "../pashto-inflector";
|
||||
import { personGender, personIsPlural } from "../../library";
|
||||
import { isLocativeAdverbEntry } from "../type-predicates";
|
||||
|
||||
export function renderEP(EP: T.EPSelectionComplete): T.EPRendered {
|
||||
const kingPerson = (EP.subject.type === "pronoun")
|
||||
? getPersonFromNP(EP.subject)
|
||||
: EP.predicate.type === "NP"
|
||||
? getPersonFromNP(EP.predicate.selection)
|
||||
: getPersonFromNP(EP.subject);
|
||||
return {
|
||||
type: "EPRendered",
|
||||
king: EP.predicate.type === "Complement" ? "subject" : "predicate",
|
||||
subject: renderNPSelection(EP.subject, false, false, "subject"),
|
||||
predicate: EP.predicate.type === "NP"
|
||||
? renderNPSelection(EP.predicate.selection, false, true, "subject")
|
||||
: renderEqCompSelection(EP.predicate.selection, kingPerson),
|
||||
equative: renderEquative(EP.equative, kingPerson),
|
||||
englishBase: equativeBuilders[EP.equative.tense](kingPerson, EP.equative.negative),
|
||||
};
|
||||
}
|
||||
|
||||
export function getEquativeForm(tense: T.EquativeTense): { hasBa: boolean, form: T.SingleOrLengthOpts<T.VerbBlock> } {
|
||||
const hasBa = (tense === "future" || tense === "wouldBe");
|
||||
const baseTense = (tense === "future")
|
||||
? "habitual"
|
||||
: tense === "wouldBe"
|
||||
? "past"
|
||||
: tense;
|
||||
return {
|
||||
hasBa,
|
||||
form: g.equativeEndings[baseTense],
|
||||
}
|
||||
}
|
||||
|
||||
function renderEquative(es: T.EquativeSelection, person: T.Person): T.EquativeRendered {
|
||||
const { form, hasBa } = getEquativeForm(es.tense)
|
||||
const ps = getPersonFromVerbForm(form, person);
|
||||
return {
|
||||
...es,
|
||||
person,
|
||||
hasBa,
|
||||
ps,
|
||||
};
|
||||
}
|
||||
|
||||
function renderEqCompSelection(s: T.EqCompSelection, person: T.Person): T.Rendered<T.EqCompSelection> {
|
||||
const e = getEnglishWord(s.entry);
|
||||
if (!e || typeof e !== "string") {
|
||||
console.log(e);
|
||||
throw new Error("error getting english for compliment");
|
||||
}
|
||||
if (isLocativeAdverbEntry(s.entry)) {
|
||||
return {
|
||||
type: "loc. adv.",
|
||||
entry: s.entry,
|
||||
ps: [psStringFromEntry(s.entry)],
|
||||
e,
|
||||
inflected: false,
|
||||
person,
|
||||
};
|
||||
}
|
||||
if (s.type === "adjective") {
|
||||
const infs = inflectWord(s.entry);
|
||||
if (!infs) return {
|
||||
type: "adjective",
|
||||
entry: s.entry,
|
||||
ps: [psStringFromEntry(s.entry)],
|
||||
e,
|
||||
inflected: false,
|
||||
person,
|
||||
}
|
||||
if (!infs.inflections || !isUnisexSet(infs.inflections)) {
|
||||
throw new Error("error getting inflections for adjective, looks like a noun's inflections");
|
||||
}
|
||||
return {
|
||||
type: "adjective",
|
||||
entry: s.entry,
|
||||
ps: chooseInflection(infs.inflections, person),
|
||||
e,
|
||||
inflected: false,
|
||||
person,
|
||||
};
|
||||
}
|
||||
throw new Error("invalid EqCompSelection");
|
||||
}
|
||||
|
||||
const equativeBuilders: Record<T.EquativeTense, (p: T.Person, n: boolean) => string[]> = {
|
||||
present: (p, n) => {
|
||||
return [
|
||||
`$SUBJ ${getEnglishConj(p, g.englishEquative.present)}${not(n)} $PRED`,
|
||||
];
|
||||
},
|
||||
habitual: (p, n) => {
|
||||
return [
|
||||
`$SUBJ ${getEnglishConj(p, g.englishEquative.present)}${not(n)} $PRED`,
|
||||
`$SUBJ tend${isThirdPersonSing(p) ? "s" : ""}${not(n)} to be $PRED`,
|
||||
];
|
||||
},
|
||||
subjunctive: (p, n) => {
|
||||
return [
|
||||
`$SUBJ ${getEnglishConj(p, g.englishEquative.present)}${not(n)} $PRED`,
|
||||
`...that $SUBJ ${getEnglishConj(p, g.englishEquative.present)}${not(n)} $PRED`,
|
||||
`$SUBJ should${not(n)} be $PRED`,
|
||||
];
|
||||
},
|
||||
future: (p, n) => {
|
||||
return [
|
||||
`$SUBJ will${not(n)} be $PRED`,
|
||||
`I betcha $SUBJ ${getEnglishConj(p, g.englishEquative.present)}${not(n)} $PRED`,
|
||||
];
|
||||
},
|
||||
past: (p, n) => {
|
||||
return [
|
||||
`$SUBJ ${getEnglishConj(p, g.englishEquative.past)}${not(n)} $PRED`,
|
||||
];
|
||||
},
|
||||
wouldBe: (p, n) => {
|
||||
return [
|
||||
`$SUBJ would ${n ? "not " : ""}be $PRED`,
|
||||
`$SUBJ would ${n ? "not " : ""}have been $PRED`,
|
||||
`$SUBJ ${getEnglishConj(p, g.englishEquative.past)} probably${not(n)} $PRED`,
|
||||
];
|
||||
},
|
||||
pastSubjunctive: () => {
|
||||
return [
|
||||
`$SUBJ should have been $PRED`,
|
||||
`(that) $SUBJ were $PRED`,
|
||||
];
|
||||
},
|
||||
}
|
||||
|
||||
function isThirdPersonSing(p: T.Person): boolean {
|
||||
return p === T.Person.ThirdSingMale || p === T.Person.ThirdPlurFemale;
|
||||
}
|
||||
function not(n: boolean): string {
|
||||
return n ? "not " : "";
|
||||
}
|
||||
|
||||
function getEnglishConj(p: T.Person, e: string | T.EnglishBlock): string {
|
||||
if (typeof e === "string") {
|
||||
return e;
|
||||
}
|
||||
const [row, col] = getVerbBlockPosFromPerson(p);
|
||||
return e[row][col];
|
||||
}
|
||||
|
||||
function chooseInflection(inflections: T.UnisexSet<T.InflectionSet>, pers: T.Person): T.ArrayOneOrMore<T.PsString> {
|
||||
const gender = personGender(pers);
|
||||
const plural = personIsPlural(pers);
|
||||
return inflections[gender][plural ? 1 : 0];
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
import * as T from "../../types";
|
||||
import { inflectWord } from "../pashto-inflector";
|
||||
import * as grammarUnits from "../grammar-units";
|
||||
import {
|
||||
getVerbBlockPosFromPerson,
|
||||
getPersonNumber,
|
||||
} from "../misc-helpers";
|
||||
import {
|
||||
concatPsString,
|
||||
psStringFromEntry,
|
||||
} from "../p-text-helpers";
|
||||
import { parseEc } from "../misc-helpers";
|
||||
import { getEnglishWord } from "../get-english-word";
|
||||
|
||||
export function renderNPSelection(NP: T.NPSelection, inflected: boolean, inflectEnglish: boolean, role: "subject"): T.Rendered<T.NPSelection>
|
||||
export function renderNPSelection(NP: T.NPSelection | T.ObjectNP, inflected: boolean, inflectEnglish: boolean, role: "object"): T.Rendered<T.NPSelection> | T.Person.ThirdPlurMale | "none";
|
||||
export function renderNPSelection(NP: T.NPSelection | T.ObjectNP, inflected: boolean, inflectEnglish: boolean, role: "subject" | "object"): T.Rendered<T.NPSelection> | T.Person.ThirdPlurMale | "none" {
|
||||
if (typeof NP !== "object") {
|
||||
if (role !== "object") {
|
||||
throw new Error("ObjectNP only allowed for objects");
|
||||
}
|
||||
return NP;
|
||||
}
|
||||
if (NP.type === "noun") {
|
||||
return renderNounSelection(NP, inflected);
|
||||
}
|
||||
if (NP.type === "pronoun") {
|
||||
return renderPronounSelection(NP, inflected, inflectEnglish);
|
||||
}
|
||||
if (NP.type === "participle") {
|
||||
return renderParticipleSelection(NP, inflected)
|
||||
}
|
||||
throw new Error("unknown NP type");
|
||||
};
|
||||
|
||||
function renderNounSelection(n: T.NounSelection, inflected: boolean): T.Rendered<T.NounSelection> {
|
||||
const english = getEnglishFromNoun(n.entry, n.number);
|
||||
const pashto = ((): T.PsString[] => {
|
||||
const infs = inflectWord(n.entry);
|
||||
const ps = n.number === "singular"
|
||||
? getInf(infs, "inflections", n.gender, false, inflected)
|
||||
: [
|
||||
...getInf(infs, "plural", n.gender, true, inflected),
|
||||
...getInf(infs, "arabicPlural", n.gender, true, inflected),
|
||||
...getInf(infs, "inflections", n.gender, true, inflected),
|
||||
];
|
||||
return ps.length > 0
|
||||
? ps
|
||||
: [psStringFromEntry(n.entry)];
|
||||
})();
|
||||
return {
|
||||
...n,
|
||||
person: getPersonNumber(n.gender, n.number),
|
||||
inflected,
|
||||
ps: pashto,
|
||||
e: english,
|
||||
};
|
||||
}
|
||||
|
||||
function renderPronounSelection(p: T.PronounSelection, inflected: boolean, englishInflected: boolean): T.Rendered<T.PronounSelection> {
|
||||
const [row, col] = getVerbBlockPosFromPerson(p.person);
|
||||
return {
|
||||
...p,
|
||||
inflected,
|
||||
ps: grammarUnits.pronouns[p.distance][inflected ? "inflected" : "plain"][row][col],
|
||||
e: grammarUnits.persons[p.person].label[englishInflected ? "object" : "subject"],
|
||||
};
|
||||
}
|
||||
|
||||
function renderParticipleSelection(p: T.ParticipleSelection, inflected: boolean): T.Rendered<T.ParticipleSelection> {
|
||||
return {
|
||||
...p,
|
||||
inflected,
|
||||
person: T.Person.ThirdPlurMale,
|
||||
// TODO: More robust inflection of inflecting pariticiples - get from the conjugation engine
|
||||
ps: [psStringFromEntry(p.verb.entry)].map(ps => inflected ? concatPsString(ps, { p: "و", f: "o" }) : ps),
|
||||
e: getEnglishParticiple(p.verb.entry),
|
||||
};
|
||||
}
|
||||
|
||||
function getInf(infs: T.InflectorOutput, t: "plural" | "arabicPlural" | "inflections", gender: T.Gender, plural: boolean, inflected: boolean): T.PsString[] {
|
||||
// TODO: make this safe!!
|
||||
// @ts-ignore
|
||||
if (infs && t in infs && infs[t] !== undefined && gender in infs[t] && infs[t][gender] !== undefined) {
|
||||
// @ts-ignore
|
||||
const iset = infs[t][gender] as T.InflectionSet;
|
||||
const inflectionNumber = (inflected ? 1 : 0) + ((t === "inflections" && plural) ? 1 : 0);
|
||||
return iset[inflectionNumber];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
function getEnglishParticiple(entry: T.DictionaryEntry): string {
|
||||
if (!entry.ec) {
|
||||
console.log("errored participle");
|
||||
console.log(entry);
|
||||
throw new Error("no english information for participle");
|
||||
}
|
||||
const ec = parseEc(entry.ec);
|
||||
const participle = ec[2];
|
||||
return (entry.ep)
|
||||
? `${participle} ${entry.ep}`
|
||||
: participle;
|
||||
}
|
||||
|
||||
function getEnglishFromNoun(entry: T.DictionaryEntry, number: T.NounNumber): string {
|
||||
const articles = {
|
||||
singular: "(a/the)",
|
||||
plural: "(the)",
|
||||
};
|
||||
const article = articles[number];
|
||||
function addArticle(s: string) {
|
||||
return `${article} ${s}`;
|
||||
}
|
||||
const e = getEnglishWord(entry);
|
||||
if (!e) throw new Error(`unable to get english from subject ${entry.f} - ${entry.ts}`);
|
||||
|
||||
if (typeof e === "string") return ` ${e}`;
|
||||
if (number === "plural") return addArticle(e.plural);
|
||||
if (!e.singular || e.singular === undefined) {
|
||||
throw new Error(`unable to get english from subject ${entry.f} - ${entry.ts}`);
|
||||
}
|
||||
return addArticle(e.singular);
|
||||
}
|
|
@ -1,17 +1,10 @@
|
|||
import * as T from "../../types";
|
||||
import { parseEc } from "../../lib/misc-helpers";
|
||||
import { inflectWord } from "../pashto-inflector";
|
||||
import { getEnglishWord } from "../get-english-word";
|
||||
import * as grammarUnits from "../grammar-units";
|
||||
import {
|
||||
getVerbBlockPosFromPerson,
|
||||
getPersonNumber,
|
||||
} from "../misc-helpers";
|
||||
import { conjugateVerb } from "../verb-conjugation";
|
||||
import {
|
||||
concatPsString,
|
||||
hasBaParticle,
|
||||
psStringFromEntry,
|
||||
getLong,
|
||||
isImperativeBlock,
|
||||
} from "../p-text-helpers";
|
||||
|
@ -28,6 +21,7 @@ import {
|
|||
} from "../type-predicates";
|
||||
import { renderEnglishVPBase } from "./english-vp-rendering";
|
||||
import { personGender } from "../../lib/misc-helpers";
|
||||
import { renderNPSelection } from "./render-np";
|
||||
|
||||
// TODO: ISSUE GETTING SPLIT HEAD NOT MATCHING WITH FUTURE VERBS
|
||||
|
||||
|
@ -67,72 +61,6 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
|
|||
};
|
||||
}
|
||||
|
||||
export function renderNPSelection(NP: T.NPSelection, inflected: boolean, inflectEnglish: boolean, role: "subject"): T.Rendered<T.NPSelection>
|
||||
export function renderNPSelection(NP: T.NPSelection | T.ObjectNP, inflected: boolean, inflectEnglish: boolean, role: "object"): T.Rendered<T.NPSelection> | T.Person.ThirdPlurMale | "none";
|
||||
export function renderNPSelection(NP: T.NPSelection | T.ObjectNP, inflected: boolean, inflectEnglish: boolean, role: "subject" | "object"): T.Rendered<T.NPSelection> | T.Person.ThirdPlurMale | "none" {
|
||||
if (typeof NP !== "object") {
|
||||
if (role !== "object") {
|
||||
throw new Error("ObjectNP only allowed for objects");
|
||||
}
|
||||
return NP;
|
||||
}
|
||||
if (NP.type === "noun") {
|
||||
return renderNounSelection(NP, inflected);
|
||||
}
|
||||
if (NP.type === "pronoun") {
|
||||
return renderPronounSelection(NP, inflected, inflectEnglish);
|
||||
}
|
||||
if (NP.type === "participle") {
|
||||
return renderParticipleSelection(NP, inflected)
|
||||
}
|
||||
throw new Error("unknown NP type");
|
||||
};
|
||||
|
||||
function renderNounSelection(n: T.NounSelection, inflected: boolean): T.Rendered<T.NounSelection> {
|
||||
const english = getEnglishFromNoun(n.entry, n.number);
|
||||
const pashto = ((): T.PsString[] => {
|
||||
const infs = inflectWord(n.entry);
|
||||
const ps = n.number === "singular"
|
||||
? getInf(infs, "inflections", n.gender, false, inflected)
|
||||
: [
|
||||
...getInf(infs, "plural", n.gender, true, inflected),
|
||||
...getInf(infs, "arabicPlural", n.gender, true, inflected),
|
||||
...getInf(infs, "inflections", n.gender, true, inflected),
|
||||
];
|
||||
return ps.length > 0
|
||||
? ps
|
||||
: [psStringFromEntry(n.entry)];
|
||||
})();
|
||||
return {
|
||||
...n,
|
||||
person: getPersonNumber(n.gender, n.number),
|
||||
inflected,
|
||||
ps: pashto,
|
||||
e: english,
|
||||
};
|
||||
}
|
||||
|
||||
function renderPronounSelection(p: T.PronounSelection, inflected: boolean, englishInflected: boolean): T.Rendered<T.PronounSelection> {
|
||||
const [row, col] = getVerbBlockPosFromPerson(p.person);
|
||||
return {
|
||||
...p,
|
||||
inflected,
|
||||
ps: grammarUnits.pronouns[p.distance][inflected ? "inflected" : "plain"][row][col],
|
||||
e: grammarUnits.persons[p.person].label[englishInflected ? "object" : "subject"],
|
||||
};
|
||||
}
|
||||
|
||||
function renderParticipleSelection(p: T.ParticipleSelection, inflected: boolean): T.Rendered<T.ParticipleSelection> {
|
||||
return {
|
||||
...p,
|
||||
inflected,
|
||||
person: T.Person.ThirdPlurMale,
|
||||
// TODO: More robust inflection of inflecting pariticiples - get from the conjugation engine
|
||||
ps: [psStringFromEntry(p.verb.entry)].map(ps => inflected ? concatPsString(ps, { p: "و", f: "o" }) : ps),
|
||||
e: getEnglishParticiple(p.verb.entry),
|
||||
};
|
||||
}
|
||||
|
||||
function renderVerbSelection(vs: T.VerbSelectionComplete, person: T.Person, objectPerson: T.Person | undefined): T.VerbRendered {
|
||||
const v = vs.dynAuxVerb || vs.verb;
|
||||
const conjugations = conjugateVerb(v.entry, v.complement);
|
||||
|
@ -267,51 +195,6 @@ function getMatrixBlock<U>(f: {
|
|||
return f[personToLabel(person)];
|
||||
}
|
||||
|
||||
function getEnglishParticiple(entry: T.DictionaryEntry): string {
|
||||
if (!entry.ec) {
|
||||
console.log("errored participle");
|
||||
console.log(entry);
|
||||
throw new Error("no english information for participle");
|
||||
}
|
||||
const ec = parseEc(entry.ec);
|
||||
const participle = ec[2];
|
||||
return (entry.ep)
|
||||
? `${participle} ${entry.ep}`
|
||||
: participle;
|
||||
}
|
||||
|
||||
function getEnglishFromNoun(entry: T.DictionaryEntry, number: T.NounNumber): string {
|
||||
const articles = {
|
||||
singular: "(a/the)",
|
||||
plural: "(the)",
|
||||
};
|
||||
const article = articles[number];
|
||||
function addArticle(s: string) {
|
||||
return `${article} ${s}`;
|
||||
}
|
||||
const e = getEnglishWord(entry);
|
||||
if (!e) throw new Error(`unable to get english from subject ${entry.f} - ${entry.ts}`);
|
||||
|
||||
if (typeof e === "string") return ` ${e}`;
|
||||
if (number === "plural") return addArticle(e.plural);
|
||||
if (!e.singular || e.singular === undefined) {
|
||||
throw new Error(`unable to get english from subject ${entry.f} - ${entry.ts}`);
|
||||
}
|
||||
return addArticle(e.singular);
|
||||
}
|
||||
|
||||
function getInf(infs: T.InflectorOutput, t: "plural" | "arabicPlural" | "inflections", gender: T.Gender, plural: boolean, inflected: boolean): T.PsString[] {
|
||||
// TODO: make this safe!!
|
||||
// @ts-ignore
|
||||
if (infs && t in infs && infs[t] !== undefined && gender in infs[t] && infs[t][gender] !== undefined) {
|
||||
// @ts-ignore
|
||||
const iset = infs[t][gender] as T.InflectionSet;
|
||||
const inflectionNumber = (inflected ? 1 : 0) + ((t === "inflections" && plural) ? 1 : 0);
|
||||
return iset[inflectionNumber];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
export function getKingAndServant(isPast: boolean, isTransitive: boolean):
|
||||
{ king: "subject", servant: "object" } |
|
||||
{ king: "object", servant: "subject" } |
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
import * as T from "../../types";
|
||||
import {
|
||||
makePsString,
|
||||
} from "../accent-and-ps-utils";
|
||||
import {
|
||||
concatPsString,
|
||||
} from "../p-text-helpers";
|
||||
// SEGMENT TOOLS
|
||||
// TODO: PULL OUT FOR SHARING ACROSS COMPILE EP ETC?
|
||||
|
||||
type SegmentDescriptions = {
|
||||
isVerbHead?: boolean,
|
||||
isOoOrWaaHead?: boolean,
|
||||
isVerbRest?: boolean,
|
||||
isMiniPronoun?: boolean,
|
||||
isKid?: boolean,
|
||||
// TODO: Simplify to just isKidAfterHead?
|
||||
isKidBetweenHeadAndRest?: boolean,
|
||||
isNu?: boolean,
|
||||
isBa?: boolean,
|
||||
}
|
||||
|
||||
type SDT = keyof SegmentDescriptions;
|
||||
export type Segment = { ps: T.PsString[] } & SegmentDescriptions & {
|
||||
adjust: (o: { ps?: T.PsString | T.PsString[] | ((ps: T.PsString) => T.PsString), desc?: SDT[] }) => Segment,
|
||||
};
|
||||
|
||||
export function makeSegment(
|
||||
ps: T.PsString | T.PsString[],
|
||||
options?: (keyof SegmentDescriptions)[],
|
||||
): Segment {
|
||||
return {
|
||||
ps: Array.isArray(ps) ? ps : [ps],
|
||||
...options && options.reduce((all, curr) => ({
|
||||
...all,
|
||||
[curr]: true,
|
||||
}), {}),
|
||||
adjust: function(o): Segment {
|
||||
return {
|
||||
...this,
|
||||
...o.ps ? {
|
||||
ps: Array.isArray(o.ps)
|
||||
? o.ps
|
||||
: "p" in o.ps
|
||||
? [o.ps]
|
||||
: this.ps.map(o.ps)
|
||||
} : {},
|
||||
...o.desc && o.desc.reduce((all, curr) => ({
|
||||
...all,
|
||||
[curr]: true,
|
||||
}), {}),
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function combineSegments(loe: (Segment | " " | "" | T.PsString)[], spaces?: "spaces"): T.PsString[] {
|
||||
const first = loe[0];
|
||||
const rest = loe.slice(1);
|
||||
if (!rest.length) {
|
||||
if (typeof first === "string" || !("ps" in first)) {
|
||||
throw new Error("can't end with a spacer");
|
||||
}
|
||||
return first.ps;
|
||||
}
|
||||
return combineSegments(rest, spaces).flatMap(r => (
|
||||
(typeof first === "object" && "ps" in first)
|
||||
? first.ps.map(f => (
|
||||
spaces ? concatPsString(f, " ", r) : concatPsString(f, r)
|
||||
))
|
||||
: [concatPsString(first, r)]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function flattenLengths(r: T.SingleOrLengthOpts<T.PsString[]>): T.PsString[] {
|
||||
if ("long" in r) {
|
||||
return Object.values(r).flat();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
export function putKidsInKidsSection(segments: Segment[], kids: Segment[]): Segment[] {
|
||||
const first = segments[0];
|
||||
const rest = segments.slice(1);
|
||||
return [
|
||||
first,
|
||||
// TODO: simplify to just isKidAfterHead ??
|
||||
...(first.isVerbHead && rest[0] && rest[0].isVerbRest)
|
||||
? kids.map(k => k.adjust({ desc: ["isKidBetweenHeadAndRest"] }))
|
||||
: kids,
|
||||
...rest,
|
||||
];
|
||||
}
|
||||
|
||||
export function splitOffLeapfrogWord(psVs: T.PsString[]): [T.PsString[], T.PsString[]] {
|
||||
return psVs.reduce((tp, ps) => {
|
||||
const pWords = ps.p.split(" ");
|
||||
const fWords = ps.f.split(" ");
|
||||
const beginning = makePsString(
|
||||
pWords.slice(0, -1).join(" "),
|
||||
fWords.slice(0, -1).join(" "),
|
||||
);
|
||||
const end = makePsString(
|
||||
pWords.slice(-1).join(" "),
|
||||
fWords.slice(-1).join(" "),
|
||||
);
|
||||
return [[...tp[0], beginning], [...tp[1], end]];
|
||||
}, [[], []] as [T.PsString[], T.PsString[]]);
|
||||
}
|
|
@ -193,9 +193,9 @@ export function removeDuplicates(psv: T.PsString[]): T.PsString[] {
|
|||
));
|
||||
}
|
||||
|
||||
export function switchSubjObj(vps: T.VPSelection): T.VPSelection;
|
||||
export function switchSubjObj(vps: T.VPSelectionState): T.VPSelectionState;
|
||||
export function switchSubjObj(vps: T.VPSelectionComplete): T.VPSelectionComplete;
|
||||
export function switchSubjObj(vps: T.VPSelection | T.VPSelectionComplete): T.VPSelection | 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")) {
|
||||
return vps;
|
||||
|
@ -222,7 +222,7 @@ export function switchSubjObj(vps: T.VPSelection | T.VPSelectionComplete): T.VPS
|
|||
};
|
||||
}
|
||||
|
||||
export function completeVPSelection(vps: T.VPSelection): T.VPSelectionComplete | undefined {
|
||||
export function completeVPSelection(vps: T.VPSelectionState): T.VPSelectionComplete | undefined {
|
||||
if (vps.subject === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -261,7 +261,7 @@ export function isThirdPerson(p: T.Person): boolean {
|
|||
);
|
||||
}
|
||||
|
||||
export function ensure2ndPersSubjPronounAndNoConflict(vps: T.VPSelection): T.VPSelection {
|
||||
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")
|
||||
|
|
|
@ -14,12 +14,12 @@ export function isAdjectiveEntry(e: T.Entry | T.DictionaryEntry): e is T.Adjecti
|
|||
return !!e.c?.includes("adj.") && !isNounEntry(e);
|
||||
}
|
||||
|
||||
export function isAdverbEntry(e: T.Entry): e is T.AdverbEntry {
|
||||
export function isAdverbEntry(e: T.Entry | T.DictionaryEntry): e is T.AdverbEntry {
|
||||
if ("entry" in e) return false;
|
||||
return !!e.c?.includes("adv.");
|
||||
}
|
||||
|
||||
export function isLocativeAdverbEntry(e: T.Entry): e is T.LocativeAdverbEntry {
|
||||
export function isLocativeAdverbEntry(e: T.Entry | T.DictionaryEntry): e is T.LocativeAdverbEntry {
|
||||
return isAdverbEntry(e) && e.c.includes("loc. adv.");
|
||||
}
|
||||
|
||||
|
|
|
@ -95,6 +95,7 @@ import {
|
|||
personIsPlural,
|
||||
personGender,
|
||||
parseEc,
|
||||
personNumber,
|
||||
} from "./lib/misc-helpers";
|
||||
import {
|
||||
simplifyPhonetics,
|
||||
|
@ -170,6 +171,7 @@ export {
|
|||
isInvalidSubjObjCombo,
|
||||
randomSubjObj,
|
||||
shuffleArray,
|
||||
personNumber,
|
||||
// protobuf helpers
|
||||
readDictionary,
|
||||
writeDictionary,
|
||||
|
|
File diff suppressed because one or more lines are too long
80
src/types.ts
80
src/types.ts
|
@ -503,7 +503,7 @@ export type Words = {
|
|||
adverbs: AdverbEntry[],
|
||||
}
|
||||
|
||||
// TODO: make this Rendered<VPSelection> with recursive Rendered<>
|
||||
// TODO: make this Rendered<VPSelectionComplete> with recursive Rendered<>
|
||||
export type VPRendered = {
|
||||
type: "VPRendered",
|
||||
king: "subject" | "object",
|
||||
|
@ -533,7 +533,7 @@ export type ModalTense = `${VerbTense}Modal`;
|
|||
export type ImperativeTense = `${Aspect}Imperative`;
|
||||
export type Tense = EquativeTense | VerbTense | PerfectTense | ModalTense | ImperativeTense;
|
||||
|
||||
export type VPSelection = {
|
||||
export type VPSelectionState = {
|
||||
subject: NPSelection | undefined,
|
||||
verb: VerbSelection,
|
||||
};
|
||||
|
@ -611,6 +611,16 @@ export type NounSelection = {
|
|||
changeNumber?: (number: NounNumber) => NounSelection,
|
||||
};
|
||||
|
||||
export type AdjectiveSelection = {
|
||||
type: "adjective",
|
||||
entry: AdjectiveEntry,
|
||||
}
|
||||
|
||||
export type LocativeAdverbSelection = {
|
||||
type: "loc. adv.",
|
||||
entry: LocativeAdverbEntry,
|
||||
}
|
||||
|
||||
// take an argument for subject/object in rendering English
|
||||
export type PronounSelection = {
|
||||
type: "pronoun",
|
||||
|
@ -630,7 +640,7 @@ export type ReplaceKey<T, K extends string, R> = T extends Record<K, unknown> ?
|
|||
|
||||
export type FormVersion = { removeKing: boolean, shrinkServant: boolean };
|
||||
|
||||
export type Rendered<T extends NPSelection> = ReplaceKey<
|
||||
export type Rendered<T extends NPSelection | EqCompSelection> = ReplaceKey<
|
||||
Omit<T, "changeGender" | "changeNumber" | "changeDistance">,
|
||||
"e",
|
||||
string
|
||||
|
@ -641,3 +651,67 @@ export type Rendered<T extends NPSelection> = ReplaceKey<
|
|||
person: Person,
|
||||
};
|
||||
// TODO: recursive changing this down into the possesor etc.
|
||||
|
||||
export type EPSelectionState = {
|
||||
subject: NPSelection | undefined,
|
||||
predicate: {
|
||||
type: "NP" | "Complement",
|
||||
NP: NPSelection | undefined,
|
||||
Complement: EqCompSelection | undefined,
|
||||
},
|
||||
equative: EquativeSelection,
|
||||
};
|
||||
|
||||
export type EPSelectionComplete = Omit<EPSelectionState, "subject" | "predicate"> & {
|
||||
subject: NPSelection,
|
||||
predicate: {
|
||||
type: "NP",
|
||||
selection: NPSelection,
|
||||
} | {
|
||||
type: "Complement",
|
||||
selection: EqCompSelection,
|
||||
},
|
||||
};
|
||||
|
||||
export type EqCompType = "adjective" | "loc. adv."; // TODO: - more
|
||||
export type EqCompSelection = AdjectiveSelection | LocativeAdverbSelection; // TODO: - more
|
||||
|
||||
export type EquativeSelection = {
|
||||
tense: EquativeTense,
|
||||
negative: boolean,
|
||||
};
|
||||
|
||||
export type EquativeRendered = EquativeSelection & {
|
||||
ps: SingleOrLengthOpts<PsString[]>,
|
||||
person: Person,
|
||||
hasBa: boolean,
|
||||
}
|
||||
|
||||
export type EPRendered = {
|
||||
type: "EPRendered",
|
||||
king: "subject" | "predicate",
|
||||
subject: Rendered<NPSelection>,
|
||||
predicate: Rendered<NPSelection | EqCompSelection>,
|
||||
equative: EquativeRendered,
|
||||
englishBase?: string[],
|
||||
}
|
||||
|
||||
export type EntryFeeder = {
|
||||
nouns: EntryLookupPortal<NounEntry>,
|
||||
verbs: EntryLookupPortal<VerbEntry>,
|
||||
adjectives: EntryLookupPortal<AdjectiveEntry>,
|
||||
locativeAdverbs: EntryLookupPortal<LocativeAdverbEntry>,
|
||||
} | {
|
||||
nouns: NounEntry[],
|
||||
verbs: VerbEntry[],
|
||||
adjectives: AdjectiveEntry[],
|
||||
locativeAdverbs: LocativeAdverbEntry[],
|
||||
}
|
||||
|
||||
export type EntryFeederSingleType<X extends VerbEntry | DictionaryEntry> = X[] | EntryLookupPortal<X>;
|
||||
|
||||
export type EntryLookupPortal<X extends VerbEntry | DictionaryEntry> = {
|
||||
search: (s: string) => X[],
|
||||
getByTs: (s: number) => X,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue