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: 1588857967561, e: `bull` }, // غوایه - ghwaayú
|
||||||
{ ts: 1527817108, e: `look, gaze, examination, inspection, spying` }, // کاته - kaatu
|
{ ts: 1527817108, e: `look, gaze, examination, inspection, spying` }, // کاته - kaatu
|
||||||
{ ts: 1527817768, e: `raven, crow` }, // کارګه - kaargu
|
{ 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: 1527818516, e: `swimming, bathing` }, // لمبېده - lambedú
|
||||||
{ ts: 1527813986, e: `sunset, west` }, // لمر پرېواته - lmarprewaatu
|
{ ts: 1527813986, e: `sunset, west` }, // لمر پرېواته - lmarprewaatu
|
||||||
{ ts: 1527813992, e: `sunset` }, // لمر لوېده - lmarlwedu
|
{ ts: 1527813992, e: `sunset` }, // لمر لوېده - lmarlwedu
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@lingdocs/pashto-inflector",
|
"name": "@lingdocs/pashto-inflector",
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"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",
|
||||||
|
|
17
src/App.tsx
17
src/App.tsx
|
@ -23,7 +23,7 @@ import {
|
||||||
Modal
|
Modal
|
||||||
} from "react-bootstrap";
|
} from "react-bootstrap";
|
||||||
import * as T from "./types";
|
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 defualtTextOptions from "./lib/default-text-options";
|
||||||
import PhraseBuilder from "./components/vp-explorer/VPExplorer";
|
import PhraseBuilder from "./components/vp-explorer/VPExplorer";
|
||||||
import useStickyState from "./lib/useStickyState";
|
import useStickyState from "./lib/useStickyState";
|
||||||
|
@ -35,6 +35,14 @@ const verbTypes: VerbType[] = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const nouns = nounsAdjs.filter(isNounEntry);
|
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[] = [
|
const transitivities: T.Transitivity[] = [
|
||||||
"transitive",
|
"transitive",
|
||||||
|
@ -47,6 +55,8 @@ const allVerbs = verbs.map((v: { entry: T.DictionaryEntry, complement?: T.Dictio
|
||||||
info: getVerbInfo(v.entry, v.complement),
|
info: getVerbInfo(v.entry, v.complement),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [verbTs, setVerbTs] = useStickyState<number>(0, "verbTs1");
|
const [verbTs, setVerbTs] = useStickyState<number>(0, "verbTs1");
|
||||||
const [verbTypeShowing, setVerbTypeShowing] = useStickyState<VerbType>("simple", "vTypeShowing");
|
const [verbTypeShowing, setVerbTypeShowing] = useStickyState<VerbType>("simple", "vTypeShowing");
|
||||||
|
@ -215,6 +225,7 @@ function App() {
|
||||||
name="verb-type"
|
name="verb-type"
|
||||||
checked={verbTypeShowing === type}
|
checked={verbTypeShowing === type}
|
||||||
value={type}
|
value={type}
|
||||||
|
onChange={() => null}
|
||||||
/>
|
/>
|
||||||
<label className="form-check-label">
|
<label className="form-check-label">
|
||||||
{type}
|
{type}
|
||||||
|
@ -244,6 +255,7 @@ function App() {
|
||||||
type="radio"
|
type="radio"
|
||||||
name="transitivity"
|
name="transitivity"
|
||||||
checked={transitivityShowing === transitivity}
|
checked={transitivityShowing === transitivity}
|
||||||
|
onChange={() => null}
|
||||||
value={transitivity}
|
value={transitivity}
|
||||||
/>
|
/>
|
||||||
<label className="form-check-label">
|
<label className="form-check-label">
|
||||||
|
@ -260,8 +272,7 @@ function App() {
|
||||||
<PhraseBuilder
|
<PhraseBuilder
|
||||||
handleLinkClick="none"
|
handleLinkClick="none"
|
||||||
verb={v.verb as T.VerbEntry}
|
verb={v.verb as T.VerbEntry}
|
||||||
nouns={nouns}
|
entryFeeder={entryFeeder}
|
||||||
verbs={verbs}
|
|
||||||
opts={textOptions}
|
opts={textOptions}
|
||||||
/>
|
/>
|
||||||
</div>}
|
</div>}
|
||||||
|
|
|
@ -27,12 +27,8 @@ export const customStyles: StylesConfig = {
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
function EntrySelect<E extends T.DictionaryEntry | T.VerbEntry>(props: ({
|
function EntrySelect<E extends T.DictionaryEntry | T.VerbEntry>(props: {
|
||||||
entries: E[]
|
entryFeeder: T.EntryFeederSingleType<E>,
|
||||||
} | {
|
|
||||||
searchF: (search: string) => E[],
|
|
||||||
getByTs: (ts: number) => E | undefined,
|
|
||||||
}) & {
|
|
||||||
value: E | undefined,
|
value: E | undefined,
|
||||||
onChange: (value: E | undefined) => void,
|
onChange: (value: E | undefined) => void,
|
||||||
name: string | undefined,
|
name: string | undefined,
|
||||||
|
@ -49,17 +45,19 @@ function EntrySelect<E extends T.DictionaryEntry | T.VerbEntry>(props: ({
|
||||||
return makeSelectOption(e, props.opts);
|
return makeSelectOption(e, props.opts);
|
||||||
}
|
}
|
||||||
const value = props.value ? makeOption(props.value) : undefined;
|
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) =>
|
const options = (searchString: string) =>
|
||||||
new Promise<{ value: string, label: string | JSX.Element }[]>(resolve => {
|
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) => {
|
const onChange = (v: { label: string | JSX.Element, value: string } | null) => {
|
||||||
if (!v) {
|
if (!v) {
|
||||||
props.onChange(undefined);
|
props.onChange(undefined);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const s = props.getByTs(parseInt(v.value));
|
const s = getByTs(parseInt(v.value));
|
||||||
if (!s) return;
|
if (!s) return;
|
||||||
props.onChange(s);
|
props.onChange(s);
|
||||||
}
|
}
|
||||||
|
@ -77,7 +75,8 @@ function EntrySelect<E extends T.DictionaryEntry | T.VerbEntry>(props: ({
|
||||||
/>
|
/>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
const options = props.entries
|
const entries = props.entryFeeder;
|
||||||
|
const options = entries
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
if ("entry" in a) {
|
if ("entry" in a) {
|
||||||
return a.entry.p.localeCompare("p" in b ? b.p : b.entry.p, "af-PS")
|
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);
|
props.onChange(undefined);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const s = props.entries.find(e => (
|
const s = entries.find(e => (
|
||||||
("entry" in e)
|
("entry" in e)
|
||||||
? e.entry.ts.toString() === v.value
|
? e.entry.ts.toString() === v.value
|
||||||
: e.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;
|
// : () => true;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
function NPNounPicker(props: ({
|
function NPNounPicker(props: {
|
||||||
nouns: T.NounEntry[],
|
entryFeeder: T.EntryFeederSingleType<T.NounEntry>,
|
||||||
} | {
|
|
||||||
nouns: (s: string) => T.NounEntry[],
|
|
||||||
getNounByTs: (ts: number) => T.NounEntry | undefined;
|
|
||||||
}) & {
|
|
||||||
noun: T.NounSelection | undefined,
|
noun: T.NounSelection | undefined,
|
||||||
onChange: (p: T.NounSelection | undefined) => void,
|
onChange: (p: T.NounSelection | undefined) => void,
|
||||||
opts: T.TextOptions,
|
opts: T.TextOptions,
|
||||||
|
@ -98,12 +94,7 @@ function NPNounPicker(props: ({
|
||||||
{!(props.noun && props.noun.dynamicComplement) ? <div>
|
{!(props.noun && props.noun.dynamicComplement) ? <div>
|
||||||
<EntrySelect
|
<EntrySelect
|
||||||
value={props.noun?.entry}
|
value={props.noun?.entry}
|
||||||
{..."getNounByTs" in props ? {
|
entryFeeder={props.entryFeeder}
|
||||||
getByTs: props.getNounByTs,
|
|
||||||
searchF: props.nouns
|
|
||||||
} : {
|
|
||||||
entries: props.nouns,
|
|
||||||
}}
|
|
||||||
onChange={onEntrySelect}
|
onChange={onEntrySelect}
|
||||||
name="Noun"
|
name="Noun"
|
||||||
opts={props.opts}
|
opts={props.opts}
|
||||||
|
|
|
@ -8,12 +8,8 @@ function makeParticipleSelection(verb: T.VerbEntry): T.ParticipleSelection {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function NPParticiplePicker(props: ({
|
function NPParticiplePicker(props: {
|
||||||
verbs: T.VerbEntry[],
|
entryFeeder: T.EntryFeederSingleType<T.VerbEntry>,
|
||||||
} | {
|
|
||||||
verbs: (s: string) => T.VerbEntry[],
|
|
||||||
getVerbByTs: (ts: number) => T.VerbEntry | undefined;
|
|
||||||
}) & {
|
|
||||||
participle: T.ParticipleSelection | undefined,
|
participle: T.ParticipleSelection | undefined,
|
||||||
onChange: (p: T.ParticipleSelection | undefined) => void,
|
onChange: (p: T.ParticipleSelection | undefined) => void,
|
||||||
opts: T.TextOptions,
|
opts: T.TextOptions,
|
||||||
|
@ -29,12 +25,7 @@ function NPParticiplePicker(props: ({
|
||||||
<h6>Participle</h6>
|
<h6>Participle</h6>
|
||||||
<EntrySelect
|
<EntrySelect
|
||||||
value={props.participle?.verb}
|
value={props.participle?.verb}
|
||||||
{..."getVerbByTs" in props ? {
|
entryFeeder={props.entryFeeder}
|
||||||
getByTs: props.getVerbByTs,
|
|
||||||
searchF: props.verbs,
|
|
||||||
} : {
|
|
||||||
entries: props.verbs,
|
|
||||||
}}
|
|
||||||
onChange={onEntrySelect}
|
onChange={onEntrySelect}
|
||||||
name="Pariticple"
|
name="Pariticple"
|
||||||
opts={props.opts}
|
opts={props.opts}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { isSecondPerson } from "../../lib/phrase-building/vp-tools";
|
||||||
const npTypes: T.NPType[] = ["pronoun", "noun", "participle"];
|
const npTypes: T.NPType[] = ["pronoun", "noun", "participle"];
|
||||||
|
|
||||||
function NPPicker(props: {
|
function NPPicker(props: {
|
||||||
heading?: JSX.Element,
|
heading?: JSX.Element | string,
|
||||||
onChange: (nps: T.NPSelection | undefined) => void,
|
onChange: (nps: T.NPSelection | undefined) => void,
|
||||||
np: T.NPSelection | undefined,
|
np: T.NPSelection | undefined,
|
||||||
counterPart: T.NPSelection | T.VerbObject | undefined,
|
counterPart: T.NPSelection | T.VerbObject | undefined,
|
||||||
|
@ -22,15 +22,8 @@ function NPPicker(props: {
|
||||||
opts: T.TextOptions,
|
opts: T.TextOptions,
|
||||||
cantClear?: boolean,
|
cantClear?: boolean,
|
||||||
is2ndPersonPicker?: boolean,
|
is2ndPersonPicker?: boolean,
|
||||||
} & ({
|
entryFeeder: T.EntryFeeder,
|
||||||
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[],
|
|
||||||
})) {
|
|
||||||
if (props.is2ndPersonPicker && ((props.np?.type !== "pronoun") || !isSecondPerson(props.np.person))) {
|
if (props.is2ndPersonPicker && ((props.np?.type !== "pronoun") || !isSecondPerson(props.np.person))) {
|
||||||
throw new Error("can't use 2ndPerson NPPicker without a pronoun");
|
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 className="d-flex flex-row justify-content-between">
|
||||||
<div></div>
|
<div></div>
|
||||||
<div>
|
<div>
|
||||||
{props.heading}
|
{typeof props.heading === "string"
|
||||||
|
? <div className="h5 text-center">{props.heading}</div>
|
||||||
|
: props.heading}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{npType && clearButton}
|
{npType && clearButton}
|
||||||
|
@ -77,7 +72,7 @@ function NPPicker(props: {
|
||||||
<div className="h6 mr-3">
|
<div className="h6 mr-3">
|
||||||
Choose NP
|
Choose NP
|
||||||
</div>
|
</div>
|
||||||
{npTypes.map((npt) => <div className="mb-2">
|
{npTypes.map((npt) => <div key={npt} className="mb-2">
|
||||||
<button
|
<button
|
||||||
key={npt}
|
key={npt}
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -98,24 +93,14 @@ function NPPicker(props: {
|
||||||
/>
|
/>
|
||||||
: npType === "noun"
|
: npType === "noun"
|
||||||
? <NounPicker
|
? <NounPicker
|
||||||
{..."getNounByTs" in props ? {
|
entryFeeder={props.entryFeeder.nouns}
|
||||||
nouns: props.nouns,
|
|
||||||
getNounByTs: props.getNounByTs,
|
|
||||||
} : {
|
|
||||||
nouns: props.nouns,
|
|
||||||
}}
|
|
||||||
noun={(props.np && props.np.type === "noun") ? props.np : undefined}
|
noun={(props.np && props.np.type === "noun") ? props.np : undefined}
|
||||||
onChange={props.onChange}
|
onChange={props.onChange}
|
||||||
opts={props.opts}
|
opts={props.opts}
|
||||||
/>
|
/>
|
||||||
: npType === "participle"
|
: npType === "participle"
|
||||||
? <ParticiplePicker
|
? <ParticiplePicker
|
||||||
{..."getVerbByTs" in props ? {
|
entryFeeder={props.entryFeeder.verbs}
|
||||||
verbs: props.verbs,
|
|
||||||
getVerbByTs: props.getVerbByTs,
|
|
||||||
} : {
|
|
||||||
verbs: props.verbs,
|
|
||||||
}}
|
|
||||||
participle={(props.np && props.np.type === "participle") ? props.np : undefined}
|
participle={(props.np && props.np.type === "participle") ? props.np : undefined}
|
||||||
onChange={props.onChange}
|
onChange={props.onChange}
|
||||||
opts={props.opts}
|
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" }}>
|
<table className="table table-bordered table-sm" style={{ textAlign: "center", minWidth: "100px", tableLayout: "fixed" }}>
|
||||||
<tbody>
|
<tbody>
|
||||||
{pSpec.map((rw, i) => (
|
{pSpec.map((rw, i) => (
|
||||||
<tr>
|
<tr key={`pronounPickerRow${i}`}>
|
||||||
{rw.map((r, j) => {
|
{rw.map((r, j) => {
|
||||||
const active = is2ndPersonPicker
|
const active = is2ndPersonPicker
|
||||||
? (p.col === j)
|
? (p.col === j)
|
||||||
: (p.row === i && p.col === j);
|
: (p.row === i && p.col === j);
|
||||||
const content = typeof r === "string" ? r : r[p.gender];
|
const content = typeof r === "string" ? r : r[p.gender];
|
||||||
return <td
|
return <td
|
||||||
|
key={`pronounPickerCell${i}${j}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleClick(is2ndPersonPicker ? 1 : i, j);
|
handleClick(is2ndPersonPicker ? 1 : i, j);
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -8,11 +8,11 @@ const options = [
|
||||||
value: "full",
|
value: "full",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: <div>Kill {roleIcon.king}</div>,
|
label: <div className="m1-2">No {roleIcon.king}</div>,
|
||||||
value: "noKing",
|
value: "noKing",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: <div>Shrink {roleIcon.servant}</div>,
|
label: <div>Mini {roleIcon.servant}</div>,
|
||||||
value: "shrinkServant",
|
value: "shrinkServant",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -109,11 +109,11 @@ export function getRandomTense(o?: T.PerfectTense | T.VerbTense | T.ModalTense |
|
||||||
}
|
}
|
||||||
|
|
||||||
function TensePicker(props: ({
|
function TensePicker(props: ({
|
||||||
vps: T.VPSelection,
|
vps: T.VPSelectionState,
|
||||||
} | {
|
} | {
|
||||||
vpsComplete: T.VPSelectionComplete,
|
vpsComplete: T.VPSelectionComplete,
|
||||||
}) & {
|
}) & {
|
||||||
onChange: (p: T.VPSelection) => void,
|
onChange: (p: T.VPSelectionState) => void,
|
||||||
mode: "charts" | "phrases" | "quiz",
|
mode: "charts" | "phrases" | "quiz",
|
||||||
}) {
|
}) {
|
||||||
const [showFormula, setShowFormula] = useStickyState<boolean>(false, "showFormula");
|
const [showFormula, setShowFormula] = useStickyState<boolean>(false, "showFormula");
|
||||||
|
@ -228,7 +228,7 @@ function TensePicker(props: ({
|
||||||
return <div>
|
return <div>
|
||||||
<div style={{ maxWidth: "300px", minWidth: "250px", margin: "0 auto" }}>
|
<div style={{ maxWidth: "300px", minWidth: "250px", margin: "0 auto" }}>
|
||||||
<div className="d-flex flex-row justify-content-between align-items-center">
|
<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)}>
|
{canHaveFormula && <div className="clickable mb-2 small" onClick={() => setShowFormula(x => !x)}>
|
||||||
🧪 {!showFormula ? "Show" : "Hide"} Formula
|
🧪 {!showFormula ? "Show" : "Hide"} Formula
|
||||||
</div>}
|
</div>}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
import useStickyState from "../../lib/useStickyState";
|
import useStickyState from "../../lib/useStickyState";
|
||||||
import Examples from "../Examples";
|
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 [form, setForm] = useStickyState<T.FormVersion>({ removeKing: false, shrinkServant: false }, "abbreviationForm");
|
||||||
const [OSV, setOSV] = useStickyState<boolean>(false, "includeOSV");
|
const [OSV, setOSV] = useStickyState<boolean>(false, "includeOSV");
|
||||||
const VPComplete = completeVPSelection(VP);
|
const VPComplete = completeVPSelection(VP);
|
||||||
|
@ -21,7 +21,7 @@ function VPDisplay({ VP, opts }: { VP: T.VPSelection, opts: T.TextOptions }) {
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
const result = compileVP(renderVP(VPComplete), { ...form, OSV });
|
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">
|
{VP.verb.transitivity === "transitive" && <div className="form-check mb-2">
|
||||||
<input
|
<input
|
||||||
className="form-check-input"
|
className="form-check-input"
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
isInvalidSubjObjCombo,
|
isInvalidSubjObjCombo,
|
||||||
} from "../../lib/phrase-building/vp-tools";
|
} from "../../lib/phrase-building/vp-tools";
|
||||||
import * as T from "../../types";
|
import * as T from "../../types";
|
||||||
import ChartDisplay from "./ChartDisplay";
|
import ChartDisplay from "./VPChartDisplay";
|
||||||
import useStickyState from "../../lib/useStickyState";
|
import useStickyState from "../../lib/useStickyState";
|
||||||
import { makeVPSelectionState } from "./verb-selection";
|
import { makeVPSelectionState } from "./verb-selection";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
@ -41,16 +41,9 @@ export function VPExplorer(props: {
|
||||||
verb: T.VerbEntry,
|
verb: T.VerbEntry,
|
||||||
opts: T.TextOptions,
|
opts: T.TextOptions,
|
||||||
handleLinkClick: ((ts: number) => void) | "none",
|
handleLinkClick: ((ts: number) => void) | "none",
|
||||||
} & ({
|
entryFeeder: T.EntryFeeder,
|
||||||
nouns: T.NounEntry[],
|
}) {
|
||||||
verbs: T.VerbEntry[],
|
const [vps, setVps] = useStickyState<T.VPSelectionState>(
|
||||||
} | {
|
|
||||||
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>(
|
|
||||||
savedVps => makeVPSelectionState(props.verb, savedVps),
|
savedVps => makeVPSelectionState(props.verb, savedVps),
|
||||||
"vpsState5",
|
"vpsState5",
|
||||||
);
|
);
|
||||||
|
@ -115,12 +108,6 @@ export function VPExplorer(props: {
|
||||||
}
|
}
|
||||||
return <div className="mt-3" style={{ maxWidth: "950px"}}>
|
return <div className="mt-3" style={{ maxWidth: "950px"}}>
|
||||||
<VerbPicker
|
<VerbPicker
|
||||||
{..."getNounByTs" in props ? {
|
|
||||||
getVerbByTs: props.getVerbByTs,
|
|
||||||
verbs: props.verbs,
|
|
||||||
} : {
|
|
||||||
verbs: props.verbs,
|
|
||||||
}}
|
|
||||||
vps={vps}
|
vps={vps}
|
||||||
onChange={quizLock(setVps)}
|
onChange={quizLock(setVps)}
|
||||||
opts={props.opts}
|
opts={props.opts}
|
||||||
|
@ -150,15 +137,7 @@ export function VPExplorer(props: {
|
||||||
heading={roles.king === "subject"
|
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: "king", item: "subject" })}>Subject {roleIcon.king}</div>
|
||||||
: <div className="h5 text-center clickable" onClick={() => setShowingExplanation({ role: "servant", item: "subject" })}>Subject {roleIcon.servant}</div>}
|
: <div className="h5 text-center clickable" onClick={() => setShowingExplanation({ role: "servant", item: "subject" })}>Subject {roleIcon.servant}</div>}
|
||||||
{..."getNounByTs" in props ? {
|
entryFeeder={props.entryFeeder}
|
||||||
getNounByTs: props.getNounByTs,
|
|
||||||
getVerbByTs: props.getVerbByTs,
|
|
||||||
nouns: props.nouns,
|
|
||||||
verbs: props.verbs,
|
|
||||||
} : {
|
|
||||||
nouns: props.nouns,
|
|
||||||
verbs: props.verbs,
|
|
||||||
}}
|
|
||||||
role={(isPast && vps.verb.transitivity !== "intransitive")
|
role={(isPast && vps.verb.transitivity !== "intransitive")
|
||||||
? "ergative"
|
? "ergative"
|
||||||
: "subject"
|
: "subject"
|
||||||
|
@ -177,15 +156,7 @@ export function VPExplorer(props: {
|
||||||
heading={roles.king === "object"
|
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: "king", item: "object" })}>Object {roleIcon.king}</div>
|
||||||
: <div className="h5 text-center clickable" onClick={() => setShowingExplanation({ role: "servant", item: "object" })}>Object {roleIcon.servant}</div>}
|
: <div className="h5 text-center clickable" onClick={() => setShowingExplanation({ role: "servant", item: "object" })}>Object {roleIcon.servant}</div>}
|
||||||
{..."getNounByTs" in props ? {
|
entryFeeder={props.entryFeeder}
|
||||||
getNounByTs: props.getNounByTs,
|
|
||||||
getVerbByTs: props.getVerbByTs,
|
|
||||||
nouns: props.nouns,
|
|
||||||
verbs: props.verbs,
|
|
||||||
} : {
|
|
||||||
nouns: props.nouns,
|
|
||||||
verbs: props.verbs,
|
|
||||||
}}
|
|
||||||
role="object"
|
role="object"
|
||||||
np={vps.verb.object}
|
np={vps.verb.object}
|
||||||
counterPart={vps.subject}
|
counterPart={vps.subject}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import playAudio from "../../lib/play-audio";
|
||||||
import TensePicker from "./TensePicker";
|
import TensePicker from "./TensePicker";
|
||||||
import Keyframes from "../Keyframes";
|
import Keyframes from "../Keyframes";
|
||||||
import energyDrink from "./energy-drink.jpg";
|
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 { concatPsString } from "../../lib/p-text-helpers";
|
||||||
import { isImperativeTense } from "../../lib/type-predicates";
|
import { isImperativeTense } from "../../lib/type-predicates";
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ type MixType = "NPs" | "tenses" | "both";
|
||||||
|
|
||||||
function VPExplorerQuiz(props: {
|
function VPExplorerQuiz(props: {
|
||||||
opts: T.TextOptions,
|
opts: T.TextOptions,
|
||||||
vps: T.VPSelection,
|
vps: T.VPSelectionState,
|
||||||
}) {
|
}) {
|
||||||
const startingQs = tickQuizState(completeVPs(props.vps));
|
const startingQs = tickQuizState(completeVPs(props.vps));
|
||||||
const [quizState, setQuizState] = useState<QuizState>(startingQs);
|
const [quizState, setQuizState] = useState<QuizState>(startingQs);
|
||||||
|
@ -370,7 +370,7 @@ function getOptionFromResult(r: {
|
||||||
return ps[0];
|
return ps[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
function completeVPs(vps: T.VPSelection): T.VPSelectionComplete {
|
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;
|
||||||
|
|
|
@ -9,8 +9,8 @@ import CompoundDisplay from "./CompoundDisplay";
|
||||||
// TODO: dark on past tense selecitons
|
// TODO: dark on past tense selecitons
|
||||||
|
|
||||||
function VerbPicker(props: {
|
function VerbPicker(props: {
|
||||||
vps: T.VPSelection,
|
vps: T.VPSelectionState,
|
||||||
onChange: (p: T.VPSelection) => void,
|
onChange: (p: T.VPSelectionState) => void,
|
||||||
opts: T.TextOptions,
|
opts: T.TextOptions,
|
||||||
handleLinkClick: ((ts: number) => void) | "none",
|
handleLinkClick: ((ts: number) => void) | "none",
|
||||||
}) {
|
}) {
|
||||||
|
|
|
@ -6,8 +6,8 @@ import { getVerbInfo } from "../../lib/verb-info";
|
||||||
|
|
||||||
export function makeVPSelectionState(
|
export function makeVPSelectionState(
|
||||||
verb: T.VerbEntry,
|
verb: T.VerbEntry,
|
||||||
os?: T.VPSelection,
|
os?: T.VPSelectionState,
|
||||||
): T.VPSelection {
|
): T.VPSelectionState {
|
||||||
const info = getVerbInfo(verb.entry, verb.complement);
|
const info = getVerbInfo(verb.entry, verb.complement);
|
||||||
const subject = (os?.verb.voice === "passive" && info.type === "dynamic compound")
|
const subject = (os?.verb.voice === "passive" && info.type === "dynamic compound")
|
||||||
? makeNounSelection(info.objComplement.entry as T.NounEntry, true)
|
? 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[] {
|
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 {
|
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: T.PsString): T.PsString;
|
||||||
export function removeAccents(s: string): string;
|
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") {
|
if (typeof s !== "string") {
|
||||||
return {
|
return {
|
||||||
...s,
|
...s,
|
||||||
|
|
|
@ -81,7 +81,7 @@ export function chooseParticipleInflection(
|
||||||
return pPartInfs; // already just one thing
|
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;
|
const base = gender === "masc" ? 4 : 5;
|
||||||
return base + (number === "singular" ? 0 : 6);
|
return base + (number === "singular" ? 0 : 6);
|
||||||
}
|
}
|
||||||
|
@ -129,10 +129,14 @@ export function getAuxTransitivity(trans: T.Transitivity): "transitive" | "intra
|
||||||
return trans === "intransitive" ? "intransitive" : "transitive";
|
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";
|
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 {
|
export function personIsPlural(person: T.Person): boolean {
|
||||||
return person > 5;
|
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: {
|
in: {
|
||||||
ts: 1527812862,
|
ts: 1527812862,
|
||||||
|
|
|
@ -49,7 +49,7 @@ export function inflectWord(word: T.DictionaryEntry): T.InflectorOutput {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (w.c && w.c.includes("pl.")) {
|
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"))) {
|
if (w.c && (w.c.includes("adj.") || w.c.includes("unisex") || w.c.includes("num"))) {
|
||||||
return handleUnisexWord(w);
|
return handleUnisexWord(w);
|
||||||
|
@ -104,7 +104,7 @@ function handleUnisexWord(word: T.DictionaryEntryNoFVars): T.InflectorOutput {
|
||||||
return false;
|
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;
|
if (!w.c || !w.c.includes("n.")) return false;
|
||||||
const plurals = makePlural(w);
|
const plurals = makePlural(w);
|
||||||
if (w.noInf) {
|
if (w.noInf) {
|
||||||
|
@ -261,7 +261,7 @@ function inflectEmphasizedYeyUnisex(p: string, f: string): T.UnisexInflections {
|
||||||
}
|
}
|
||||||
|
|
||||||
function inflectConsonantEndingUnisex(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
|
const iBase = fSyls.length === 1
|
||||||
? makePsString(p, accentFSylsOnNFromEnd(fSyls, 0))
|
? makePsString(p, accentFSylsOnNFromEnd(fSyls, 0))
|
||||||
: makePsString(p, f);
|
: 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 {
|
import {
|
||||||
concatPsString,
|
concatPsString,
|
||||||
} from "../p-text-helpers";
|
} from "../p-text-helpers";
|
||||||
import { makePsString } from "../accent-and-ps-utils";
|
import {
|
||||||
|
Segment,
|
||||||
|
makeSegment,
|
||||||
|
flattenLengths,
|
||||||
|
combineSegments,
|
||||||
|
splitOffLeapfrogWord,
|
||||||
|
putKidsInKidsSection,
|
||||||
|
} from "./segment";
|
||||||
import {
|
import {
|
||||||
removeAccents,
|
removeAccents,
|
||||||
} from "../accent-helpers";
|
} 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[][] {
|
function arrangeVerbWNegative(head: T.PsString | undefined, restRaw: T.PsString[], V: T.VerbRendered): Segment[][] {
|
||||||
const hasLeapfrog = isPerfectTense(V.tense) || isModalTense(V.tense);
|
const hasLeapfrog = isPerfectTense(V.tense) || isModalTense(V.tense);
|
||||||
const rest = (() => {
|
const rest = (() => {
|
||||||
|
@ -299,91 +293,3 @@ function compileEnglish(VP: T.VPRendered): string[] | undefined {
|
||||||
: 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 * 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 {
|
import {
|
||||||
getVerbBlockPosFromPerson,
|
getVerbBlockPosFromPerson,
|
||||||
getPersonNumber,
|
|
||||||
} from "../misc-helpers";
|
} from "../misc-helpers";
|
||||||
import { conjugateVerb } from "../verb-conjugation";
|
import { conjugateVerb } from "../verb-conjugation";
|
||||||
import {
|
import {
|
||||||
concatPsString,
|
|
||||||
hasBaParticle,
|
hasBaParticle,
|
||||||
psStringFromEntry,
|
|
||||||
getLong,
|
getLong,
|
||||||
isImperativeBlock,
|
isImperativeBlock,
|
||||||
} from "../p-text-helpers";
|
} from "../p-text-helpers";
|
||||||
|
@ -28,6 +21,7 @@ import {
|
||||||
} from "../type-predicates";
|
} from "../type-predicates";
|
||||||
import { renderEnglishVPBase } from "./english-vp-rendering";
|
import { renderEnglishVPBase } from "./english-vp-rendering";
|
||||||
import { personGender } from "../../lib/misc-helpers";
|
import { personGender } from "../../lib/misc-helpers";
|
||||||
|
import { renderNPSelection } from "./render-np";
|
||||||
|
|
||||||
// TODO: ISSUE GETTING SPLIT HEAD NOT MATCHING WITH FUTURE VERBS
|
// 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 {
|
function renderVerbSelection(vs: T.VerbSelectionComplete, person: T.Person, objectPerson: T.Person | undefined): T.VerbRendered {
|
||||||
const v = vs.dynAuxVerb || vs.verb;
|
const v = vs.dynAuxVerb || vs.verb;
|
||||||
const conjugations = conjugateVerb(v.entry, v.complement);
|
const conjugations = conjugateVerb(v.entry, v.complement);
|
||||||
|
@ -267,51 +195,6 @@ function getMatrixBlock<U>(f: {
|
||||||
return f[personToLabel(person)];
|
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):
|
export function getKingAndServant(isPast: boolean, isTransitive: boolean):
|
||||||
{ king: "subject", servant: "object" } |
|
{ king: "subject", servant: "object" } |
|
||||||
{ king: "object", servant: "subject" } |
|
{ 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.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 ("tenseCategory" in vps.verb) {
|
||||||
if (!vps.subject || !(typeof vps.verb.object === "object") || (vps.verb.tenseCategory === "imperative")) {
|
if (!vps.subject || !(typeof vps.verb.object === "object") || (vps.verb.tenseCategory === "imperative")) {
|
||||||
return vps;
|
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) {
|
if (vps.subject === undefined) {
|
||||||
return 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);
|
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.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);
|
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;
|
if ("entry" in e) return false;
|
||||||
return !!e.c?.includes("adv.");
|
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.");
|
return isAdverbEntry(e) && e.c.includes("loc. adv.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -95,6 +95,7 @@ import {
|
||||||
personIsPlural,
|
personIsPlural,
|
||||||
personGender,
|
personGender,
|
||||||
parseEc,
|
parseEc,
|
||||||
|
personNumber,
|
||||||
} from "./lib/misc-helpers";
|
} from "./lib/misc-helpers";
|
||||||
import {
|
import {
|
||||||
simplifyPhonetics,
|
simplifyPhonetics,
|
||||||
|
@ -170,6 +171,7 @@ export {
|
||||||
isInvalidSubjObjCombo,
|
isInvalidSubjObjCombo,
|
||||||
randomSubjObj,
|
randomSubjObj,
|
||||||
shuffleArray,
|
shuffleArray,
|
||||||
|
personNumber,
|
||||||
// protobuf helpers
|
// protobuf helpers
|
||||||
readDictionary,
|
readDictionary,
|
||||||
writeDictionary,
|
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[],
|
adverbs: AdverbEntry[],
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make this Rendered<VPSelection> with recursive Rendered<>
|
// TODO: make this Rendered<VPSelectionComplete> with recursive Rendered<>
|
||||||
export type VPRendered = {
|
export type VPRendered = {
|
||||||
type: "VPRendered",
|
type: "VPRendered",
|
||||||
king: "subject" | "object",
|
king: "subject" | "object",
|
||||||
|
@ -533,7 +533,7 @@ 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 VPSelection = {
|
export type VPSelectionState = {
|
||||||
subject: NPSelection | undefined,
|
subject: NPSelection | undefined,
|
||||||
verb: VerbSelection,
|
verb: VerbSelection,
|
||||||
};
|
};
|
||||||
|
@ -611,6 +611,16 @@ export type NounSelection = {
|
||||||
changeNumber?: (number: NounNumber) => 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
|
// take an argument for subject/object in rendering English
|
||||||
export type PronounSelection = {
|
export type PronounSelection = {
|
||||||
type: "pronoun",
|
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 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">,
|
Omit<T, "changeGender" | "changeNumber" | "changeDistance">,
|
||||||
"e",
|
"e",
|
||||||
string
|
string
|
||||||
|
@ -641,3 +651,67 @@ export type Rendered<T extends NPSelection> = ReplaceKey<
|
||||||
person: Person,
|
person: Person,
|
||||||
};
|
};
|
||||||
// TODO: recursive changing this down into the possesor etc.
|
// 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