This commit is contained in:
parent
5c283f2cea
commit
b4a84645ea
|
@ -5,7 +5,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^5.15.4",
|
"@fortawesome/fontawesome-free": "^5.15.4",
|
||||||
"@lingdocs/lingdocs-main": "^0.2.0",
|
"@lingdocs/lingdocs-main": "^0.2.0",
|
||||||
"@lingdocs/pashto-inflector": "^2.0.5",
|
"@lingdocs/pashto-inflector": "^2.3.2",
|
||||||
"@testing-library/jest-dom": "^5.11.4",
|
"@testing-library/jest-dom": "^5.11.4",
|
||||||
"@testing-library/react": "^11.1.0",
|
"@testing-library/react": "^11.1.0",
|
||||||
"@testing-library/user-event": "^12.1.10",
|
"@testing-library/user-event": "^12.1.10",
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const fetch = require("node-fetch");
|
const fetch = require("node-fetch");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const { readDictionary } = require("@lingdocs/pashto-inflector");
|
|
||||||
const verbsPath = path.join(".", "src", "words");
|
const verbsPath = path.join(".", "src", "words");
|
||||||
const pChars = [
|
const pChars = [
|
||||||
"آ",
|
"آ",
|
||||||
|
@ -51,10 +50,11 @@ const pChars = [
|
||||||
'ږ',
|
'ږ',
|
||||||
];
|
];
|
||||||
|
|
||||||
fetch(process.env.LINGDOCS_DICTIONARY_URL).then(res => res.arrayBuffer()).then(data => {
|
|
||||||
const { entries } = readDictionary(data);
|
fetch(process.env.LINGDOCS_DICTIONARY_URL + ".json").then(res => res.json()).then(data => {
|
||||||
|
const { entries } = data;
|
||||||
const filtered = shuffle(entries.filter(e => (
|
const filtered = shuffle(entries.filter(e => (
|
||||||
e.c?.includes("n. f.") && e.p.endsWith("ي")
|
e.c?.includes("loc. adv.")
|
||||||
)));
|
)));
|
||||||
const content = `module.exports = [
|
const content = `module.exports = [
|
||||||
${filtered.reduce((text, entry) => (
|
${filtered.reduce((text, entry) => (
|
||||||
|
|
|
@ -4,11 +4,8 @@ import {
|
||||||
EntrySelect,
|
EntrySelect,
|
||||||
Types as T,
|
Types as T,
|
||||||
} from "@lingdocs/pashto-inflector";
|
} from "@lingdocs/pashto-inflector";
|
||||||
import {
|
|
||||||
nouns,
|
|
||||||
verbs,
|
|
||||||
} from "../words/words";
|
|
||||||
import { useStickyState } from "@lingdocs/pashto-inflector";
|
import { useStickyState } from "@lingdocs/pashto-inflector";
|
||||||
|
import entryFeeder from "../lib/entry-feeder";
|
||||||
|
|
||||||
function VPBuilder() {
|
function VPBuilder() {
|
||||||
const [entry, setEntry] = useStickyState<T.VerbEntry | undefined>(undefined, "vEntrySelect");
|
const [entry, setEntry] = useStickyState<T.VerbEntry | undefined>(undefined, "vEntrySelect");
|
||||||
|
@ -18,7 +15,7 @@ function VPBuilder() {
|
||||||
<EntrySelect
|
<EntrySelect
|
||||||
value={entry}
|
value={entry}
|
||||||
onChange={setEntry}
|
onChange={setEntry}
|
||||||
entries={verbs}
|
entryFeeder={entryFeeder.verbs}
|
||||||
opts={defaultTextOptions}
|
opts={defaultTextOptions}
|
||||||
isVerbSelect
|
isVerbSelect
|
||||||
name="Verb"
|
name="Verb"
|
||||||
|
@ -29,8 +26,8 @@ function VPBuilder() {
|
||||||
? <VPExplorer
|
? <VPExplorer
|
||||||
verb={entry}
|
verb={entry}
|
||||||
opts={defaultTextOptions}
|
opts={defaultTextOptions}
|
||||||
nouns={nouns}
|
entryFeeder={entryFeeder}
|
||||||
verbs={verbs}
|
handleLinkClick="none"
|
||||||
/>
|
/>
|
||||||
: <div className="lead">
|
: <div className="lead">
|
||||||
Choose a verb to start building
|
Choose a verb to start building
|
||||||
|
|
|
@ -1,155 +0,0 @@
|
||||||
import {
|
|
||||||
VerbTable,
|
|
||||||
defaultTextOptions as opts,
|
|
||||||
ButtonSelect,
|
|
||||||
Types as T,
|
|
||||||
personGender,
|
|
||||||
personIsPlural,
|
|
||||||
typePredicates,
|
|
||||||
} from "@lingdocs/pashto-inflector";
|
|
||||||
import {
|
|
||||||
ExplorerState,
|
|
||||||
ExplorerReducerAction,
|
|
||||||
} from "./explorer-types";
|
|
||||||
import {
|
|
||||||
equativeMachine,
|
|
||||||
assembleEquativeOutput,
|
|
||||||
} from "../../lib/equative-machine";
|
|
||||||
const {
|
|
||||||
isUnisexNounEntry,
|
|
||||||
isAdjectiveEntry,
|
|
||||||
isVerbEntry,
|
|
||||||
isLocativeAdverbEntry,
|
|
||||||
isNounEntry,
|
|
||||||
} = typePredicates;
|
|
||||||
|
|
||||||
function chooseLength<O>(o: T.SingleOrLengthOpts<O>, length: "short" | "long"): O {
|
|
||||||
return ("long" in o) ? o[length] : o;
|
|
||||||
}
|
|
||||||
|
|
||||||
function SingleItemDisplay({ state }: { state: ExplorerState }) {
|
|
||||||
if (state.subject.type === "pronouns") {
|
|
||||||
return <div>ERROR: Wrong display being used</div>;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const se = state.subject[state.subject.type];
|
|
||||||
const pe = state.predicate[state.predicate.type];
|
|
||||||
const subject = makeNounPhrase(se, state, "subject");
|
|
||||||
const predicate = (isAdjectiveEntry(pe) || isLocativeAdverbEntry(pe))
|
|
||||||
? makeComplement(pe)
|
|
||||||
: makeNounPhrase(pe, state, "predicate");
|
|
||||||
const block = assembleEquativeOutput(
|
|
||||||
equativeMachine({
|
|
||||||
subject,
|
|
||||||
predicate,
|
|
||||||
tense: state.tense,
|
|
||||||
negative: state.negative,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
return <div>
|
|
||||||
<VerbTable textOptions={opts} block={chooseLength(block, state.length)} />
|
|
||||||
</div>;
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
return <div>Error making equative sentence</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeComplement(entry: T.AdjectiveEntry | T.LocativeAdverbEntry): Compliment {
|
|
||||||
return {
|
|
||||||
type: "compliment",
|
|
||||||
entry,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeNounPhrase(entry: T.NounEntry | T.UnisexNounEntry | T.VerbEntry, state: ExplorerState, entity: "subject" | "predicate"): NounPhrase {
|
|
||||||
if (isVerbEntry(entry)) {
|
|
||||||
return {
|
|
||||||
type: "participle",
|
|
||||||
entry,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
type: "noun",
|
|
||||||
number: state[entity].info.number,
|
|
||||||
gender: state[entity].info.gender,
|
|
||||||
entry,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function makeBlockWPronouns(e: T.AdjectiveEntry | T.UnisexNounEntry | T.LocativeAdverbEntry, tense: EquativeTense, negative: boolean, length?: "short" | "long"): T.SingleOrLengthOpts<T.VerbBlock> {
|
|
||||||
// if the output's gonna have long / short forms (if it's past or wouldBe) then recursive call to make the long and short versions
|
|
||||||
if (!length && "long" in assembleEquativeOutput(equativeMachine({
|
|
||||||
subject: { type: "pronoun", pronounType: "near", person: 0 },
|
|
||||||
predicate: (isAdjectiveEntry(e) || isLocativeAdverbEntry(e))
|
|
||||||
? { type: "compliment", entry: e }
|
|
||||||
: { type: "noun", gender: "masc", number: "singular", entry: e },
|
|
||||||
tense,
|
|
||||||
negative,
|
|
||||||
}))) {
|
|
||||||
return {
|
|
||||||
short: makeBlockWPronouns(e, tense, negative, "short") as T.VerbBlock,
|
|
||||||
long: makeBlockWPronouns(e, tense, negative, "long") as T.VerbBlock,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const makeP = (p: T.Person): T.ArrayOneOrMore<T.PsString> => {
|
|
||||||
const b = assembleEquativeOutput(equativeMachine({
|
|
||||||
subject: { type: "pronoun", pronounType: "far", person: p },
|
|
||||||
predicate: (isAdjectiveEntry(e) || isLocativeAdverbEntry(e))
|
|
||||||
? { type: "compliment", entry: e }
|
|
||||||
: { type: "noun", gender: personGender(p), number: personIsPlural(p) ? "plural" : "singular", entry: e },
|
|
||||||
tense,
|
|
||||||
negative,
|
|
||||||
}));
|
|
||||||
if ("long" in b) {
|
|
||||||
if (!length) throw new Error("bad length processing");
|
|
||||||
return b[length];
|
|
||||||
}
|
|
||||||
return b;
|
|
||||||
};
|
|
||||||
return [
|
|
||||||
[makeP(0), makeP(6)],
|
|
||||||
[makeP(1), makeP(7)],
|
|
||||||
[makeP(2), makeP(8)],
|
|
||||||
[makeP(3), makeP(9)],
|
|
||||||
[makeP(4), makeP(10)],
|
|
||||||
[makeP(5), makeP(11)],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
function PronounBlockDisplay({ state }: { state: ExplorerState }) {
|
|
||||||
const pred = state.predicate[state.predicate.type];
|
|
||||||
if (!isVerbEntry(pred) && (isAdjectiveEntry(pred) || isLocativeAdverbEntry(pred) || (isNounEntry(pred) && isUnisexNounEntry(pred)))) {
|
|
||||||
const block = makeBlockWPronouns(pred, state.tense, state.negative);
|
|
||||||
return <VerbTable
|
|
||||||
textOptions={opts}
|
|
||||||
block={chooseLength(block, state.length)}
|
|
||||||
/>;
|
|
||||||
}
|
|
||||||
return <div>Invalid combination</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
function EquativeDisplay({ state, dispatch }: { state: ExplorerState, dispatch: (action: ExplorerReducerAction) => void }) {
|
|
||||||
return <>
|
|
||||||
{(state.tense === "past" || state.tense === "wouldBe") && <div className="text-center">
|
|
||||||
<ButtonSelect
|
|
||||||
small
|
|
||||||
options={[
|
|
||||||
{ label: "Long", value: "long" },
|
|
||||||
{ label: "Short", value: "short" },
|
|
||||||
]}
|
|
||||||
value={state.length}
|
|
||||||
handleChange={(p) => dispatch({ type: "setLength", payload: p as "long" | "short" })}
|
|
||||||
/>
|
|
||||||
</div>}
|
|
||||||
{state.subject.type === "pronouns"
|
|
||||||
? <PronounBlockDisplay state={state} />
|
|
||||||
: <SingleItemDisplay state={state} />
|
|
||||||
}
|
|
||||||
{state.predicate.type === "participle" && <div className="mt-2 small text-muted text-center">
|
|
||||||
Note: This means that the subject <em>is</em> the act of the participle/verb, not that the subject is currently doing the verb!
|
|
||||||
</div>}
|
|
||||||
</>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default EquativeDisplay;
|
|
|
@ -1,70 +0,0 @@
|
||||||
import { useState } from "react";
|
|
||||||
import {
|
|
||||||
reducer,
|
|
||||||
} from "./explorer-reducer";
|
|
||||||
import {
|
|
||||||
InputSelector,
|
|
||||||
TenseSelector,
|
|
||||||
} from "./explorer-selectors";
|
|
||||||
import {
|
|
||||||
ExplorerState,
|
|
||||||
ExplorerReducerAction,
|
|
||||||
} from "./explorer-types";
|
|
||||||
import {
|
|
||||||
defaultUnisexNoun,
|
|
||||||
defaultAdjective,
|
|
||||||
defaultNoun,
|
|
||||||
defaultParticiple,
|
|
||||||
defaultAdverb,
|
|
||||||
} from "./explorer-inputs";
|
|
||||||
import EquativeDisplay from "./EquativeDisplay";
|
|
||||||
|
|
||||||
const defaultState: ExplorerState = {
|
|
||||||
tense: "present",
|
|
||||||
length: "short",
|
|
||||||
negative: false,
|
|
||||||
predicate: {
|
|
||||||
type: "adjective",
|
|
||||||
adjective: defaultAdjective,
|
|
||||||
adverb: defaultAdverb,
|
|
||||||
unisexNoun: defaultUnisexNoun,
|
|
||||||
participle: defaultParticiple,
|
|
||||||
noun: defaultNoun,
|
|
||||||
info: {
|
|
||||||
number: "singular",
|
|
||||||
gender: "masc",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
subject: {
|
|
||||||
type: "pronouns",
|
|
||||||
noun: defaultNoun,
|
|
||||||
participle: defaultParticiple,
|
|
||||||
unisexNoun: defaultUnisexNoun,
|
|
||||||
info: {
|
|
||||||
number: "singular",
|
|
||||||
gender: "masc",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function EquativeExplorer() {
|
|
||||||
const [state, unsafeSetState] = useState<ExplorerState>(defaultState);
|
|
||||||
function dispatch(action: ExplorerReducerAction) {
|
|
||||||
const newState = reducer(state, action);
|
|
||||||
unsafeSetState(newState);
|
|
||||||
}
|
|
||||||
return <>
|
|
||||||
<TenseSelector state={state} dispatch={dispatch} />
|
|
||||||
<div className="row">
|
|
||||||
<div className="col">
|
|
||||||
<InputSelector entity="subject" state={state} dispatch={dispatch} />
|
|
||||||
</div>
|
|
||||||
<div className="col">
|
|
||||||
<InputSelector entity="predicate" state={state} dispatch={dispatch} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<EquativeDisplay state={state} dispatch={dispatch} />
|
|
||||||
</>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default EquativeExplorer;
|
|
|
@ -1,37 +0,0 @@
|
||||||
import { nouns, adjectives, verbs, adverbs } from "../../words/words";
|
|
||||||
import {
|
|
||||||
Types as T,
|
|
||||||
typePredicates as tp,
|
|
||||||
} from "@lingdocs/pashto-inflector";
|
|
||||||
|
|
||||||
function sort<T extends (T.AdjectiveEntry | T.NounEntry | T.VerbEntry | T.AdverbEntry)>(arr: Readonly<T[]>): T[] {
|
|
||||||
if ("entry" in arr[0]) {
|
|
||||||
return [...arr].sort((a, b) => (
|
|
||||||
// @ts-ignore
|
|
||||||
a.entry.p.localeCompare(b.entry.p)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return [...arr].sort((a, b) => (
|
|
||||||
// @ts-ignore
|
|
||||||
a.p.localeCompare(b.p)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
const unisexNouns = sort(nouns.filter(x => tp.isUnisexNounEntry(x)) as T.UnisexNounEntry[]);
|
|
||||||
const nonUnisexNouns = sort(nouns.filter(x => !tp.isUnisexNounEntry(x)) as (T.MascNounEntry | T.FemNounEntry)[]);
|
|
||||||
|
|
||||||
const inputs = {
|
|
||||||
adjective: sort(adjectives),
|
|
||||||
unisexNoun: unisexNouns,
|
|
||||||
noun: nonUnisexNouns,
|
|
||||||
participle: sort(verbs),
|
|
||||||
adverb: sort(adverbs.filter(tp.isLocativeAdverbEntry)),
|
|
||||||
};
|
|
||||||
|
|
||||||
export const defaultAdjective = inputs.adjective.find(ps => ps.p === "زوړ") || inputs.adjective[0];
|
|
||||||
export const defaultAdverb = inputs.adverb.find(ps => ps.p === "دلته") || inputs.adverb[0];
|
|
||||||
export const defaultUnisexNoun = inputs.unisexNoun.find(ps => ps.p === "پښتون") || inputs.unisexNoun[0];
|
|
||||||
export const defaultNoun = inputs.noun.find(ps => ps.p === "کتاب") || inputs.noun[0];
|
|
||||||
export const defaultParticiple = inputs.participle.find(ps => ps.entry.p === "لیکل") || inputs.participle[0];
|
|
||||||
|
|
||||||
export default inputs;
|
|
|
@ -1,110 +0,0 @@
|
||||||
import inputs from "./explorer-inputs";
|
|
||||||
import { ExplorerState, ExplorerReducerAction } from "./explorer-types";
|
|
||||||
import {
|
|
||||||
Types as T,
|
|
||||||
} from "@lingdocs/pashto-inflector";
|
|
||||||
|
|
||||||
export function reducer(state: ExplorerState, action: ExplorerReducerAction): ExplorerState {
|
|
||||||
if (action.type === "setPredicate") {
|
|
||||||
const pile = inputs[state.predicate.type] as (T.UnisexNounEntry | T.AdjectiveEntry)[];
|
|
||||||
const predicate = (pile.find(p => p.ts === action.payload) || pile[0]);
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
predicate: {
|
|
||||||
...state.predicate,
|
|
||||||
[state.predicate.type]: predicate,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (action.type === "setPredicateType") {
|
|
||||||
const predicateType = action.payload;
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
predicate: {
|
|
||||||
...state.predicate,
|
|
||||||
type: predicateType,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (action.type === "setSubjectType") {
|
|
||||||
const subjectType = action.payload;
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
subject: {
|
|
||||||
...state.subject,
|
|
||||||
type: subjectType,
|
|
||||||
},
|
|
||||||
predicate: {
|
|
||||||
...state.predicate,
|
|
||||||
type: (
|
|
||||||
subjectType === "pronouns" &&
|
|
||||||
!["adjective", "adverb", "unisexNoun"].includes(state.predicate.type)
|
|
||||||
) ? "adjective" : state.predicate.type,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (action.type === "setSubject") {
|
|
||||||
if (state.subject.type === "pronouns") return state;
|
|
||||||
const pile = inputs[state.subject.type];
|
|
||||||
// @ts-ignore
|
|
||||||
const subject = (pile.find(p => p.ts === action.payload) || pile[0]);
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
subject: {
|
|
||||||
...state.subject,
|
|
||||||
[state.subject.type]: subject,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (action.type === "setNumber") {
|
|
||||||
const entity = action.payload.entity;
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
[entity]: {
|
|
||||||
...state[entity],
|
|
||||||
info: {
|
|
||||||
...state[entity].info,
|
|
||||||
number: action.payload.number,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (action.type === "setGender") {
|
|
||||||
const entity = action.payload.entity;
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
[entity]: {
|
|
||||||
...state[entity],
|
|
||||||
info: {
|
|
||||||
...state[entity].info,
|
|
||||||
gender: action.payload.gender,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (action.type === "setTense") {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
tense: action.payload,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// if (action.type === "setPredicateEntity") {
|
|
||||||
// return {
|
|
||||||
// ...state,
|
|
||||||
// predicate: {
|
|
||||||
// ...state.predicate,
|
|
||||||
// entity: action.payload,
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
if (action.type === "setNegative") {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
negative: action.payload,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
length: action.payload,
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,233 +0,0 @@
|
||||||
// import { makeOptionLabel } from "./explorer-helpers";
|
|
||||||
import inputs from "./explorer-inputs";
|
|
||||||
import {
|
|
||||||
ExplorerReducerAction,
|
|
||||||
ExplorerState,
|
|
||||||
SubjectType,
|
|
||||||
PredicateType,
|
|
||||||
} from "./explorer-types";
|
|
||||||
import {
|
|
||||||
getEnglishParticiple,
|
|
||||||
} from "../../lib/np-tools";
|
|
||||||
import {
|
|
||||||
ButtonSelect,
|
|
||||||
getEnglishWord,
|
|
||||||
Types as T,
|
|
||||||
removeFVarients,
|
|
||||||
typePredicates,
|
|
||||||
} from "@lingdocs/pashto-inflector";
|
|
||||||
import Select from "react-select";
|
|
||||||
const {
|
|
||||||
isAdjectiveEntry,
|
|
||||||
isAdverbEntry,
|
|
||||||
isFemNounEntry,
|
|
||||||
isMascNounEntry,
|
|
||||||
isNounEntry,
|
|
||||||
isPluralNounEntry,
|
|
||||||
} = typePredicates;
|
|
||||||
|
|
||||||
const zIndexProps = {
|
|
||||||
menuPortalTarget: document.body,
|
|
||||||
styles: { menuPortal: (base: any) => ({ ...base, zIndex: 9999 }) },
|
|
||||||
};
|
|
||||||
|
|
||||||
const npTypeOptions: { type: SubjectType, label: string }[] = [
|
|
||||||
{ type: "unisexNoun", label: "Unisex Noun"},
|
|
||||||
{ type: "noun", label: "Noun" },
|
|
||||||
{ type: "participle", label: "Participle" },
|
|
||||||
];
|
|
||||||
const subjectTypeOptions: { type: SubjectType, label: string }[] = [
|
|
||||||
{ type: "pronouns" as SubjectType, label: "Pronouns" }
|
|
||||||
];
|
|
||||||
const compTypeOptions: { type: PredicateType, label: string }[] = [
|
|
||||||
{ type: "adjective", label: "Adjective" },
|
|
||||||
{ type: "adverb", label: "Loc. Adverb" },
|
|
||||||
];
|
|
||||||
|
|
||||||
export function InputSelector({ state, dispatch, entity }: {
|
|
||||||
state: ExplorerState,
|
|
||||||
dispatch: (action: ExplorerReducerAction) => void,
|
|
||||||
entity: "subject" | "predicate",
|
|
||||||
}) {
|
|
||||||
const typeOptions = [
|
|
||||||
...entity === "subject"
|
|
||||||
? subjectTypeOptions
|
|
||||||
: compTypeOptions,
|
|
||||||
...npTypeOptions.filter(o => !(entity === "predicate" && (
|
|
||||||
(state.subject.type === "pronouns" && ["noun", "participle"].includes(o.type))
|
|
||||||
// || (state.subject.type === "unisexNoun" && o.type === "unisexNoun")
|
|
||||||
))),
|
|
||||||
];
|
|
||||||
|
|
||||||
function onTypeSelect(e: React.ChangeEvent<HTMLInputElement>) {
|
|
||||||
if (entity === "subject") {
|
|
||||||
const t = e.target.value as SubjectType;
|
|
||||||
dispatch({ type: "setSubjectType", payload: t });
|
|
||||||
} else {
|
|
||||||
const t = e.target.value as PredicateType;
|
|
||||||
dispatch({ type: "setPredicateType", payload: t });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onEntrySelect({ value }: any) {
|
|
||||||
dispatch({ type: entity === "subject" ? "setSubject" : "setPredicate", payload: parseInt(value) });
|
|
||||||
}
|
|
||||||
|
|
||||||
function CheckboxItem({ type, label }: { type: string, label: string }) {
|
|
||||||
const id = `${entity}-${type}-radio`;
|
|
||||||
return <div className="form-check">
|
|
||||||
<input
|
|
||||||
className="form-check-input"
|
|
||||||
type="radio"
|
|
||||||
id={id}
|
|
||||||
value={type}
|
|
||||||
checked={state[entity].type === type}
|
|
||||||
onChange={onTypeSelect}
|
|
||||||
/>
|
|
||||||
<label className="form-check-label" htmlFor={id}>
|
|
||||||
{label}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
const type = state[entity].type;
|
|
||||||
const entry: T.NounEntry | T.VerbEntry | T.AdjectiveEntry | T.LocativeAdverbEntry | undefined = type === "pronouns"
|
|
||||||
? undefined
|
|
||||||
// @ts-ignore
|
|
||||||
: state[entity][type];
|
|
||||||
const options = type === "pronouns"
|
|
||||||
? []
|
|
||||||
: inputs[type].map(makeOption);
|
|
||||||
|
|
||||||
return <div className="form-group">
|
|
||||||
<h5 className="mb-2">{entity === "subject" ? "Subject:" : "Predicate:"}</h5>
|
|
||||||
<div className="mb-2">
|
|
||||||
{typeOptions.map(({ type, label }) => (
|
|
||||||
<CheckboxItem type={type} label={label} key={`${entity}-${type}-radio`} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
{type !== "pronouns" && <>
|
|
||||||
<Select
|
|
||||||
value={entry && ("entry" in entry ? entry.entry : entry).ts.toString()}
|
|
||||||
onChange={onEntrySelect}
|
|
||||||
className="mb-2"
|
|
||||||
// @ts-ignore
|
|
||||||
options={options}
|
|
||||||
isSearchable
|
|
||||||
// @ts-ignore
|
|
||||||
placeholder={options.find(o => o.value === ("entry" in entry ? entry.entry : entry).ts.toString())?.label}
|
|
||||||
{...zIndexProps}
|
|
||||||
/>
|
|
||||||
{!["adjective", "adverb"].includes(type) && !(state.subject.type === "pronouns" && state.predicate.type === "unisexNoun") &&
|
|
||||||
<GenderAndNumberSelect state={state} dispatch={dispatch} entity={entity} />
|
|
||||||
}
|
|
||||||
</>}
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function GenderAndNumberSelect({ state, dispatch, entity }: {
|
|
||||||
state: ExplorerState,
|
|
||||||
dispatch: (action: ExplorerReducerAction) => void,
|
|
||||||
entity: "subject" | "predicate",
|
|
||||||
}) {
|
|
||||||
const type = state[entity].type;
|
|
||||||
if (type === "pronouns") {
|
|
||||||
return <div>ERROR: Should not display with pronouns</div>;
|
|
||||||
}
|
|
||||||
// @ts-ignore
|
|
||||||
const entry: NounEntry | VerbEntry | AdverbEntry | AdjectiveEntry = state[entity][type];
|
|
||||||
const gender = type === "noun"
|
|
||||||
? (isNounEntry(entry) && isMascNounEntry(entry) ? "masc" : "fem")
|
|
||||||
: type === "participle"
|
|
||||||
? "masc"
|
|
||||||
: state[entity].info.gender;
|
|
||||||
const pluralNounSelected = (
|
|
||||||
type === "noun" && isPluralNounEntry(state[entity][type])
|
|
||||||
);
|
|
||||||
return <div className="d-flex flex-row justify-content-center mt-3">
|
|
||||||
<div className="mr-2">
|
|
||||||
<ButtonSelect
|
|
||||||
small
|
|
||||||
options={[
|
|
||||||
...(type === "unisexNoun" || (type === "participle") || (isNounEntry(entry) && isMascNounEntry(entry)))
|
|
||||||
? [{ label: "Masc.", value: "masc" }] : [],
|
|
||||||
...(type === "unisexNoun" || ((type !== "participle") && (isNounEntry(entry) && isFemNounEntry(entry))))
|
|
||||||
? [{ label: "Fem.", value: "fem" }] : [],
|
|
||||||
]}
|
|
||||||
value={gender}
|
|
||||||
handleChange={type === "noun" ? p => null : (p) => dispatch({ type: "setGender", payload: { gender: p as T.Gender, entity }})}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="ml-2">
|
|
||||||
<ButtonSelect
|
|
||||||
small
|
|
||||||
options={[
|
|
||||||
...(!pluralNounSelected && type !== "participle") ? [{ label: "Singular", value: "singular" }] : [],
|
|
||||||
{ label: "Plural", value: "plural" },
|
|
||||||
]}
|
|
||||||
value={(state[entity].info.number === "plural" || pluralNounSelected || type === "participle") ? "plural" : "singular"}
|
|
||||||
handleChange={(p) => dispatch({ type: "setNumber", payload: { number: p as NounNumber, entity }})}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeOption(e: T.VerbEntry | T.NounEntry | T.AdjectiveEntry | T.LocativeAdverbEntry): { value: string, label: string } {
|
|
||||||
const entry = "entry" in e ? e.entry : e;
|
|
||||||
// TODO: THIS IS SUUUPER SKETCH
|
|
||||||
const eng = (isNounEntry(e) || isAdjectiveEntry(e) || isAdverbEntry(e))
|
|
||||||
? getEnglishWord(e)
|
|
||||||
: getEnglishParticiple(entry);
|
|
||||||
const english = typeof eng === "string"
|
|
||||||
? eng
|
|
||||||
: !eng
|
|
||||||
? ""
|
|
||||||
: ("singular" in eng && eng.singular !== undefined)
|
|
||||||
? eng.singular
|
|
||||||
: eng.plural;
|
|
||||||
return {
|
|
||||||
label: `${entry.p} - ${removeFVarients(entry.f)} (${english})`,
|
|
||||||
value: entry.ts.toString(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function TenseSelector({ state, dispatch }: {
|
|
||||||
state: ExplorerState,
|
|
||||||
dispatch: (action: ExplorerReducerAction) => void,
|
|
||||||
}) {
|
|
||||||
const options: { value: EquativeTense, label: string }[] = [
|
|
||||||
{ value: "present", label: "Present" },
|
|
||||||
{ value: "habitual", label: "Habitual" },
|
|
||||||
{ value: "subjunctive", label: "Subjunctive" },
|
|
||||||
{ value: "past", label: "Past" },
|
|
||||||
{ value: "future", label: "Future" },
|
|
||||||
{ value: "wouldBe", label: '"Would Be"' },
|
|
||||||
{ value: "pastSubjunctive", label: "Past Subjunctive" },
|
|
||||||
];
|
|
||||||
function onTenseSelect({ value }: any) {
|
|
||||||
dispatch({ type: "setTense", payload: value });
|
|
||||||
}
|
|
||||||
return <div>
|
|
||||||
<h5>Equative:</h5>
|
|
||||||
<Select
|
|
||||||
value={state.tense}
|
|
||||||
onChange={onTenseSelect}
|
|
||||||
className="mb-2"
|
|
||||||
// @ts-ignore
|
|
||||||
options={options}
|
|
||||||
placeholder={options.find(o => o.value === state.tense)?.label}
|
|
||||||
{...zIndexProps}
|
|
||||||
/>
|
|
||||||
<div className="text-center mb-2">
|
|
||||||
<ButtonSelect
|
|
||||||
small
|
|
||||||
options={[
|
|
||||||
{ label: "Pos.", value: "pos" },
|
|
||||||
{ label: "Neg.", value: "neg" },
|
|
||||||
]}
|
|
||||||
value={state.negative ? "neg" : "pos"}
|
|
||||||
handleChange={(p) => dispatch({ type: "setNegative", payload: p === "neg" })}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
import { Types as T } from "@lingdocs/pashto-inflector";
|
|
||||||
|
|
||||||
export type PredicateNPType = "noun" | "unisexNoun" | "participle";
|
|
||||||
export type PredicateCompType = "adjective" | "adverb";
|
|
||||||
export type PredicateType = PredicateNPType | PredicateCompType;
|
|
||||||
export type SubjectType = "pronouns" | "noun" | "unisexNoun" | "participle";
|
|
||||||
|
|
||||||
export type ExplorerState = {
|
|
||||||
tense: EquativeTense,
|
|
||||||
length: "short" | "long",
|
|
||||||
subject: SubjectEntityInfo,
|
|
||||||
predicate: PredicateEntityInfo,
|
|
||||||
negative: boolean,
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SubjectEntityInfo = EntitiyInfo & { type: SubjectType };
|
|
||||||
|
|
||||||
export type PredicateEntityInfo = EntitiyInfo & {
|
|
||||||
type: PredicateType,
|
|
||||||
adjective: import("@lingdocs/pashto-inflector").Types.AdjectiveEntry,
|
|
||||||
adverb: import("@lingdocs/pashto-inflector").Types.LocativeAdverbEntry,
|
|
||||||
}
|
|
||||||
|
|
||||||
type EntitiyInfo = {
|
|
||||||
noun: import("@lingdocs/pashto-inflector").Types.NounEntry,
|
|
||||||
participle: import("@lingdocs/pashto-inflector").Types.VerbEntry,
|
|
||||||
unisexNoun: import("@lingdocs/pashto-inflector").Types.UnisexNounEntry,
|
|
||||||
info: {
|
|
||||||
number: NounNumber,
|
|
||||||
gender: T.Gender,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ExplorerReducerAction = {
|
|
||||||
type: "setPredicateType", payload: PredicateType,
|
|
||||||
} | {
|
|
||||||
type: "setPredicate", payload: number,
|
|
||||||
} | {
|
|
||||||
type: "setSubjectType", payload: SubjectType,
|
|
||||||
} | {
|
|
||||||
type: "setSubject", payload: number,
|
|
||||||
} | {
|
|
||||||
type: "setNumber", payload: { entity: "subject" | "predicate", number: NounNumber },
|
|
||||||
} | {
|
|
||||||
type: "setGender", payload: { entity: "subject" | "predicate", gender: T.Gender },
|
|
||||||
} | {
|
|
||||||
type: "setTense", payload: EquativeTense,
|
|
||||||
} | {
|
|
||||||
type: "setLength", payload: "short" | "long",
|
|
||||||
} | {
|
|
||||||
type: "setNegative", payload: boolean,
|
|
||||||
};
|
|
|
@ -2,17 +2,15 @@
|
||||||
title: Equative Explorer 🌎
|
title: Equative Explorer 🌎
|
||||||
---
|
---
|
||||||
|
|
||||||
import EquativeExplorer from "../../components/equative-explorer/EquativeExplorer";
|
import {
|
||||||
import { useState } from "react";
|
EPExplorer,
|
||||||
|
defaultTextOptions,
|
||||||
You can use this tool to explore how to make different equative sentences. Everything that comes out of this will be **gramatically correct**, but the sentences might not always make sense! 🤪
|
} from "@lingdocs/pashto-inflector";
|
||||||
|
import entryFeeder from "../../lib/entry-feeder";
|
||||||
|
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<EquativeExplorer />
|
<EPExplorer
|
||||||
|
entryFeeder={entryFeeder}
|
||||||
|
opts={defaultTextOptions}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
### Equative Rules: 👨🏫
|
|
||||||
|
|
||||||
- The **subject** has to be a **noun phrase**
|
|
||||||
- The **predicate** can be a **noun phrase** or **compliment**
|
|
||||||
- In Pashto, if the predicate is a noun phrase, the equative agrees with the predicate. Otherwise, if the predicate is a compliment, the equative agrees with the subject. In English, the equative always agrees with the subject.
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Types as T } from "@lingdocs/pashto-inflector";
|
||||||
|
import {
|
||||||
|
nouns,
|
||||||
|
verbs,
|
||||||
|
adjectives,
|
||||||
|
locativeAdverbs,
|
||||||
|
} from "../words/words";
|
||||||
|
|
||||||
|
const entryFeeder: T.EntryFeeder = {
|
||||||
|
nouns,
|
||||||
|
verbs,
|
||||||
|
adjectives,
|
||||||
|
locativeAdverbs,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default entryFeeder;
|
|
@ -1,6 +1,15 @@
|
||||||
// TODO: I think this is just used for the old equative explorer
|
// TODO: I think this is just used for the old equative explorer
|
||||||
// build a new better equative explorer using NP Pickers etc and deprecate all this
|
// build a new better equative explorer using NP Pickers etc and deprecate all this
|
||||||
|
|
||||||
|
type EPSelection = {
|
||||||
|
subject: NPSelection,
|
||||||
|
predicate: NPSelection | EComplementSelection,
|
||||||
|
equative: {
|
||||||
|
tense: EquativeTense,
|
||||||
|
negative: boolean,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
type EquativeTense = "present" | "subjunctive" | "habitual" | "past" | "future" | "wouldBe" | "pastSubjunctive";
|
type EquativeTense = "present" | "subjunctive" | "habitual" | "past" | "future" | "wouldBe" | "pastSubjunctive";
|
||||||
type NounNumber = "singular" | "plural";
|
type NounNumber = "singular" | "plural";
|
||||||
|
|
||||||
|
|
|
@ -5,16 +5,23 @@ import {
|
||||||
} from "@lingdocs/pashto-inflector";
|
} from "@lingdocs/pashto-inflector";
|
||||||
import { categorize } from "../lib/categorize";
|
import { categorize } from "../lib/categorize";
|
||||||
|
|
||||||
const words = categorize<T.Entry, T.Words>(rawWords, {
|
const words = categorize<T.Entry, {
|
||||||
"nouns": tp.isNounEntry,
|
nouns: T.NounEntry[],
|
||||||
"adjectives": tp.isAdjectiveEntry,
|
adjectives: T.AdjectiveEntry[],
|
||||||
"verbs": tp.isVerbEntry,
|
verbs: T.VerbEntry[],
|
||||||
"adverbs": tp.isAdverbEntry,
|
adverbs: T.AdverbEntry[],
|
||||||
|
locativeAdverbs: T.LocativeAdverbEntry[],
|
||||||
|
}>(rawWords, {
|
||||||
|
nouns: tp.isNounEntry,
|
||||||
|
adjectives: tp.isAdjectiveEntry,
|
||||||
|
verbs: tp.isVerbEntry,
|
||||||
|
adverbs: tp.isAdverbEntry,
|
||||||
|
locativeAdverbs: tp.isLocativeAdverbEntry,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default words;
|
export default words;
|
||||||
|
|
||||||
export const { nouns, adjectives, verbs, adverbs } = words;
|
export const { nouns, adjectives, verbs, adverbs, locativeAdverbs } = words;
|
||||||
|
|
||||||
// console.log(
|
// console.log(
|
||||||
// Object.entries(
|
// Object.entries(
|
||||||
|
|
|
@ -21,6 +21,11 @@
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"noFallthroughCasesInSwitch": true
|
"noFallthroughCasesInSwitch": true
|
||||||
},
|
},
|
||||||
|
"ts-node": {
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src"
|
"src"
|
||||||
]
|
]
|
||||||
|
|
|
@ -1684,10 +1684,10 @@
|
||||||
pbf "^3.2.1"
|
pbf "^3.2.1"
|
||||||
rambda "^6.7.0"
|
rambda "^6.7.0"
|
||||||
|
|
||||||
"@lingdocs/pashto-inflector@^2.0.5":
|
"@lingdocs/pashto-inflector@^2.3.2":
|
||||||
version "2.0.5"
|
version "2.3.2"
|
||||||
resolved "https://npm.lingdocs.com/@lingdocs%2fpashto-inflector/-/pashto-inflector-2.0.5.tgz#b917428e81395206d78f8f72122dddc4432364b3"
|
resolved "https://npm.lingdocs.com/@lingdocs%2fpashto-inflector/-/pashto-inflector-2.3.2.tgz#59c42d5bbe4e2befc467cd506b12a11632545eaf"
|
||||||
integrity sha512-WdkunOYqj3045Too4PioDIh9ljYXrbGiV+CVZtbF8s8Bnw/L+6Y7ShgYFb2DQalkwtis+5BJzzcKMMUE4ZtTYQ==
|
integrity sha512-EGWHwe8RdRD7NcZtE4CVUQGSvfXCcVyTrNmmqv1kJeRLZIn2MN2UMCbGRMZcz3681mCQvvfkRpsAD8xJSSjyNw==
|
||||||
dependencies:
|
dependencies:
|
||||||
classnames "^2.2.6"
|
classnames "^2.2.6"
|
||||||
pbf "^3.2.1"
|
pbf "^3.2.1"
|
||||||
|
|
Loading…
Reference in New Issue