refactor and improve equative explorer, using the NP centric data format
This commit is contained in:
parent
8843a7f106
commit
1d9ff2b9e1
|
@ -5,7 +5,7 @@
|
|||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^5.15.4",
|
||||
"@lingdocs/lingdocs-main": "^0.2.0",
|
||||
"@lingdocs/pashto-inflector": "^1.3.2",
|
||||
"@lingdocs/pashto-inflector": "^1.3.5",
|
||||
"@testing-library/jest-dom": "^5.11.4",
|
||||
"@testing-library/react": "^11.1.0",
|
||||
"@testing-library/user-event": "^12.1.10",
|
||||
|
|
|
@ -4,10 +4,14 @@ const { readDictionary } = require("@lingdocs/pashto-inflector");
|
|||
const path = require("path");
|
||||
const wordsPath = path.join(".", "src", "words");
|
||||
const wordsFile = "raw-words.ts";
|
||||
|
||||
const verbCollectionPath = path.join(wordsPath, "verb-categories");
|
||||
const nounAdjCollectionPath = path.join(wordsPath, "noun-adj-categories");
|
||||
const adverbCollectionPath = path.join(wordsPath, "adverbs");
|
||||
|
||||
const verbTsFiles = fs.readdirSync(verbCollectionPath);
|
||||
const nounAdjTsFiles = fs.readdirSync(nounAdjCollectionPath);
|
||||
const adverbTsFiles = fs.readdirSync(adverbCollectionPath);
|
||||
|
||||
const allVerbTsS = verbTsFiles.flatMap(fileName => [
|
||||
...require(path.join("..", verbCollectionPath, fileName)).map(x => x.ts)
|
||||
|
@ -17,6 +21,10 @@ const allNounAdjTsS = nounAdjTsFiles.flatMap(fileName => [
|
|||
...require(path.join("..", nounAdjCollectionPath, fileName)).map(x => x.ts)
|
||||
]).filter((v, i, a) => a.findIndex(x => x === v) === i);
|
||||
|
||||
const allAdverbTsS = adverbTsFiles.flatMap(fileName => [
|
||||
...require(path.join("..", adverbCollectionPath, fileName)).map(x => x.ts)
|
||||
]).filter((v, i, a) => a.findIndex(x => x === v) === i);
|
||||
|
||||
console.log("getting words from dictionary...");
|
||||
|
||||
fetch(process.env.LINGDOCS_DICTIONARY_URL).then(res => res.arrayBuffer()).then(buffer => {
|
||||
|
@ -61,7 +69,7 @@ function getVerbsFromTsS(entries) {
|
|||
}
|
||||
|
||||
function getNounsAdjsFromTsS(entries) {
|
||||
const b = allNounAdjTsS.map(ts => {
|
||||
const b = [...allNounAdjTsS, ...allAdverbTsS].map(ts => {
|
||||
const entry = entries.find(x => ts === x.ts);
|
||||
if (!entry) {
|
||||
console.log("couldn't find ts", ts);
|
||||
|
|
|
@ -3,20 +3,11 @@ const fetch = require("node-fetch");
|
|||
const path = require("path");
|
||||
const { readDictionary } = require("@lingdocs/pashto-inflector");
|
||||
const verbsPath = path.join(".", "src", "words");
|
||||
const collectionPath = path.join(verbsPath, "verb-categories");
|
||||
const verbTsFiles = fs.readdirSync(collectionPath);
|
||||
|
||||
const pConsonants = ["ب", "پ", "ت", "ټ", "ث", "ج", "چ", "ح", "خ", "څ", "ځ", "د", "ډ", "ذ", "ر", "ړ", "ز", "ژ", "ږ", "س", "ش", "ښ", "ص", "ض", "ط", "ظ", "غ", "ف", "ق", "ک", "ګ", "گ", "ل", "ل", "م", "ن", "ڼ"];
|
||||
|
||||
// const allTsS = [...new Set(verbTsFiles.reduce((arr, fileName) => {
|
||||
// const TsS = require(path.join("..", collectionPath, fileName));
|
||||
// return [...arr, ...TsS];
|
||||
// }, []))];
|
||||
|
||||
fetch(process.env.LINGDOCS_DICTIONARY_URL).then(res => res.arrayBuffer()).then(data => {
|
||||
const { entries } = readDictionary(data);
|
||||
const filtered = entries.filter(e => (
|
||||
e.c?.includes("n. m. unisex")
|
||||
e.c?.includes("loc. adv.")
|
||||
));
|
||||
const content = `module.exports = [
|
||||
${filtered.reduce((text, entry) => (
|
||||
|
@ -25,21 +16,3 @@ ${filtered.reduce((text, entry) => (
|
|||
];`;
|
||||
fs.writeFileSync(path.join(verbsPath, "query-results.js"), content);
|
||||
});
|
||||
|
||||
// function getFromTsS(entries) {
|
||||
// return allTsS.map(item => {
|
||||
// const entry = entries.find(x => item.ts === x.ts);
|
||||
// if (!entry) {
|
||||
// console.log("couldn't find ts", ts);
|
||||
// return undefined;
|
||||
// }
|
||||
// if (entry.c && entry.c.includes("comp.")) {
|
||||
// const complement = entries.find(x => entry.l === x.ts);
|
||||
// return {
|
||||
// entry,
|
||||
// complement,
|
||||
// };
|
||||
// }
|
||||
// return { entry, def: item.e };
|
||||
// }).filter(x => x);
|
||||
// }
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
getEnglishWord,
|
||||
} from "@lingdocs/pashto-inflector";
|
||||
|
||||
function InflectionCarousel({ items }: { items: (Noun | Adjective)[] }) {
|
||||
function InflectionCarousel({ items }: { items: (NounEntry | AdjectiveEntry)[] }) {
|
||||
if (!items.length) {
|
||||
return "no items for carousel";
|
||||
}
|
||||
|
|
|
@ -3,25 +3,31 @@ import {
|
|||
defaultTextOptions as opts,
|
||||
ButtonSelect,
|
||||
Types as T,
|
||||
personGender,
|
||||
personIsPlural,
|
||||
} from "@lingdocs/pashto-inflector";
|
||||
import {
|
||||
ExplorerState,
|
||||
ExplorerReducerAction,
|
||||
} from "./explorer-types";
|
||||
import {
|
||||
makeBlockWPronouns,
|
||||
} from "./explorer-helpers";
|
||||
// import {
|
||||
// makeBlockWPronouns,
|
||||
// } from "./explorer-helpers";
|
||||
import {
|
||||
equativeMachine,
|
||||
assembleEquativeOutput,
|
||||
SubjectInput,
|
||||
PredicateInput,
|
||||
isParticipleInput,
|
||||
ParticipleInput,
|
||||
} from "../../lib/equative-machine";
|
||||
import { isPluralEntry, isUnisexNoun, isAdjective, isSingularEntry } from "../../lib/type-predicates";
|
||||
import {
|
||||
isPluralNounEntry,
|
||||
isUnisexNounEntry,
|
||||
isAdjectiveEntry,
|
||||
isSingularEntry,
|
||||
isVerbEntry,
|
||||
isLocativeAdverbEntry,
|
||||
isNounEntry,
|
||||
} from "../../lib/type-predicates";
|
||||
|
||||
export function chooseLength<O>(o: T.SingleOrLengthOpts<O>, length: "short" | "long"): O {
|
||||
function chooseLength<O>(o: T.SingleOrLengthOpts<O>, length: "short" | "long"): O {
|
||||
return ("long" in o) ? o[length] : o;
|
||||
}
|
||||
|
||||
|
@ -30,10 +36,18 @@ function SingleItemDisplay({ state }: { state: ExplorerState }) {
|
|||
return <div>ERROR: Wrong display being used</div>;
|
||||
}
|
||||
try {
|
||||
const subjInput = makeSubjectInput(state.subject[state.subject.type], state);
|
||||
const predInput = makePredicateInput(state.predicate[state.predicate.type], state);
|
||||
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(subjInput, predInput, state.tense)
|
||||
equativeMachine({
|
||||
subject,
|
||||
predicate,
|
||||
tense: state.tense,
|
||||
})
|
||||
);
|
||||
return <div>
|
||||
<VerbTable textOptions={opts} block={chooseLength(block, state.length)} />
|
||||
|
@ -44,84 +58,99 @@ function SingleItemDisplay({ state }: { state: ExplorerState }) {
|
|||
}
|
||||
}
|
||||
|
||||
function makeSubjectInput(entry: Noun | ParticipleInput | UnisexNoun, state: ExplorerState): SubjectInput {
|
||||
if (isParticipleInput(entry)) {
|
||||
return entry;
|
||||
}
|
||||
const isUnisex = isUnisexNoun(entry);
|
||||
if (isUnisex && isSingularEntry(entry)) {
|
||||
function makeComplement(entry: AdjectiveEntry | LocativeAdverbEntry): Compliment {
|
||||
return {
|
||||
type: "compliment",
|
||||
entry,
|
||||
};
|
||||
}
|
||||
|
||||
function makeNounPhrase(entry: NounEntry | UnisexNounEntry | VerbEntry, state: ExplorerState, entity: "subject" | "predicate"): NounPhrase {
|
||||
if (isVerbEntry(entry)) {
|
||||
return {
|
||||
...state.subject.info,
|
||||
type: "participle",
|
||||
entry,
|
||||
};
|
||||
}
|
||||
if (isUnisex && isPluralEntry(entry)) {
|
||||
const isUnisex = isUnisexNounEntry(entry);
|
||||
if (isUnisex && isSingularEntry(entry)) {
|
||||
return {
|
||||
...state.subject.info,
|
||||
plural: true,
|
||||
type: "unisex noun",
|
||||
number: state[entity].info.number,
|
||||
gender: state[entity].info.gender,
|
||||
entry,
|
||||
};
|
||||
}
|
||||
if (isUnisex && isPluralNounEntry(entry)) {
|
||||
return {
|
||||
type: "unisex noun",
|
||||
number: state[entity].info.number,
|
||||
gender: state[entity].info.gender,
|
||||
entry,
|
||||
};
|
||||
}
|
||||
if (isUnisex) {
|
||||
throw new Error("improper unisex noun");
|
||||
}
|
||||
if (isPluralEntry(entry)) {
|
||||
if (isPluralNounEntry(entry)) {
|
||||
const e = entry as PluralNounEntry<MascNounEntry | FemNounEntry>;
|
||||
return {
|
||||
plural: true,
|
||||
entry,
|
||||
}
|
||||
type: "plural noun",
|
||||
entry: e,
|
||||
};
|
||||
}
|
||||
if (isSingularEntry(entry)) {
|
||||
const e = entry as SingularEntry<MascNounEntry | FemNounEntry>;
|
||||
return {
|
||||
entry,
|
||||
plural: state.subject.info.plural,
|
||||
type: "singular noun",
|
||||
entry: e,
|
||||
number: state[entity].info.number,
|
||||
};
|
||||
}
|
||||
throw new Error("unable to make subject input from entry");
|
||||
}
|
||||
|
||||
function makePredicateInput(entry: Noun | ParticipleInput | UnisexNoun | Adjective, state: ExplorerState): PredicateInput {
|
||||
if (isParticipleInput(entry) || isAdjective(entry)) {
|
||||
return entry;
|
||||
}
|
||||
const isUnisex = isUnisexNoun(entry);
|
||||
if (isUnisex && state.subject.type === "pronouns") {
|
||||
return entry;
|
||||
}
|
||||
if (isUnisex && isSingularEntry(entry)) {
|
||||
export function makeBlockWPronouns(e: AdjectiveEntry | UnisexNounEntry | LocativeAdverbEntry, tense: EquativeTense, 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: "unisex noun", gender: "masc", number: "singular", entry: e },
|
||||
tense,
|
||||
}))) {
|
||||
return {
|
||||
...state.predicate.info,
|
||||
entry,
|
||||
short: makeBlockWPronouns(e, tense, "short") as T.VerbBlock,
|
||||
long: makeBlockWPronouns(e, tense, "long") as T.VerbBlock,
|
||||
};
|
||||
}
|
||||
if (isUnisex && isPluralEntry(entry)) {
|
||||
return {
|
||||
...state.predicate.info,
|
||||
plural: true,
|
||||
entry,
|
||||
};
|
||||
}
|
||||
if (isUnisex) {
|
||||
throw new Error("improper unisex noun");
|
||||
}
|
||||
if (isPluralEntry(entry)) {
|
||||
return {
|
||||
plural: true,
|
||||
entry,
|
||||
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: "unisex noun", gender: personGender(p), number: personIsPlural(p) ? "plural" : "singular", entry: e },
|
||||
tense,
|
||||
}));
|
||||
if ("long" in b) {
|
||||
if (!length) throw new Error("bad length processing");
|
||||
return b[length];
|
||||
}
|
||||
}
|
||||
if (isSingularEntry(entry)) {
|
||||
return {
|
||||
entry,
|
||||
plural: state.predicate.info.plural,
|
||||
};
|
||||
}
|
||||
throw new Error("unable to make predicate input from entry");
|
||||
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 (!isParticipleInput(pred) && (isAdjective(pred) || isUnisexNoun(pred))) {
|
||||
if (!isVerbEntry(pred) && (isAdjectiveEntry(pred) || isLocativeAdverbEntry(pred) || (isNounEntry(pred) && isUnisexNounEntry(pred)))) {
|
||||
const block = makeBlockWPronouns(pred, state.tense);
|
||||
return <VerbTable
|
||||
textOptions={opts}
|
||||
|
@ -133,7 +162,7 @@ function PronounBlockDisplay({ state }: { state: ExplorerState }) {
|
|||
|
||||
function EquativeDisplay({ state, dispatch }: { state: ExplorerState, dispatch: (action: ExplorerReducerAction) => void }) {
|
||||
return <>
|
||||
{state.tense === "past" && <div className="text-center">
|
||||
{(state.tense === "past" || state.tense === "wouldBe") && <div className="text-center">
|
||||
<ButtonSelect
|
||||
small
|
||||
options={[
|
||||
|
@ -148,6 +177,9 @@ function EquativeDisplay({ state, dispatch }: { state: ExplorerState, dispatch:
|
|||
? <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>}
|
||||
</>;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@ import {
|
|||
reducer,
|
||||
} from "./explorer-reducer";
|
||||
import {
|
||||
PredicateSelector,
|
||||
SubjectSelector,
|
||||
InputSelector,
|
||||
TenseSelector,
|
||||
} from "./explorer-selectors";
|
||||
import {
|
||||
|
@ -16,6 +15,7 @@ import {
|
|||
defaultAdjective,
|
||||
defaultNoun,
|
||||
defaultParticiple,
|
||||
defaultAdverb,
|
||||
} from "./explorer-inputs";
|
||||
import EquativeDisplay from "./EquativeDisplay";
|
||||
|
||||
|
@ -25,11 +25,12 @@ const defaultState: ExplorerState = {
|
|||
predicate: {
|
||||
type: "adjective",
|
||||
adjective: defaultAdjective,
|
||||
adverb: defaultAdverb,
|
||||
unisexNoun: defaultUnisexNoun,
|
||||
participle: defaultParticiple,
|
||||
noun: defaultNoun,
|
||||
info: {
|
||||
plural: false,
|
||||
number: "singular",
|
||||
gender: "masc",
|
||||
},
|
||||
},
|
||||
|
@ -39,7 +40,7 @@ const defaultState: ExplorerState = {
|
|||
participle: defaultParticiple,
|
||||
unisexNoun: defaultUnisexNoun,
|
||||
info: {
|
||||
plural: false,
|
||||
number: "singular",
|
||||
gender: "masc",
|
||||
},
|
||||
},
|
||||
|
@ -52,15 +53,13 @@ function EquativeExplorer() {
|
|||
unsafeSetState(newState);
|
||||
}
|
||||
return <>
|
||||
<TenseSelector state={state} dispatch={dispatch} />
|
||||
<div className="row">
|
||||
<div className="col-sm">
|
||||
<TenseSelector state={state} dispatch={dispatch} />
|
||||
<div className="col">
|
||||
<InputSelector entity="subject" state={state} dispatch={dispatch} />
|
||||
</div>
|
||||
<div className="col">
|
||||
<SubjectSelector state={state} dispatch={dispatch} />
|
||||
</div>
|
||||
<div className="col">
|
||||
<PredicateSelector state={state} dispatch={dispatch} />
|
||||
<InputSelector entity="predicate" state={state} dispatch={dispatch} />
|
||||
</div>
|
||||
</div>
|
||||
<EquativeDisplay state={state} dispatch={dispatch} />
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
import {
|
||||
Types as T,
|
||||
removeFVarients,
|
||||
getEnglishWord,
|
||||
} from "@lingdocs/pashto-inflector";
|
||||
import {
|
||||
equativeMachine,
|
||||
assembleEquativeOutput,
|
||||
ParticipleInput,
|
||||
isParticipleInput,
|
||||
getEnglishParticiple,
|
||||
} from "../../lib/equative-machine";
|
||||
|
||||
export function sort<T extends (Adjective | Noun | ParticipleInput)>(arr: Readonly<T[]>): T[] {
|
||||
return [...arr].sort((a, b) => a.p.localeCompare(b.p));
|
||||
}
|
||||
|
||||
export function makeBlockWPronouns(e: Adjective | UnisexNoun, tense: EquativeTense, 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(0, e, tense))) {
|
||||
return {
|
||||
short: makeBlockWPronouns(e, tense, "short") as T.VerbBlock,
|
||||
long: makeBlockWPronouns(e, tense, "long") as T.VerbBlock,
|
||||
};
|
||||
}
|
||||
const makeP = (p: T.Person): T.ArrayOneOrMore<T.PsString> => {
|
||||
const b = assembleEquativeOutput(equativeMachine(p, e, tense));
|
||||
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)],
|
||||
];
|
||||
}
|
||||
|
||||
export function makeOptionLabel(e: T.DictionaryEntry): string {
|
||||
const eng = (isParticipleInput(e)) ? getEnglishParticiple(e) : getEnglishWord(e);
|
||||
const english = typeof eng === "string"
|
||||
? eng
|
||||
: !eng
|
||||
? ""
|
||||
: ("singular" in eng && eng.singular !== undefined)
|
||||
? eng.singular
|
||||
: eng.plural;
|
||||
return `${e.p} - ${removeFVarients(e.f)} (${english})`;
|
||||
}
|
|
@ -1,27 +1,37 @@
|
|||
import { nouns, adjectives, verbs } from "../../words/words";
|
||||
import { nouns, adjectives, verbs, adverbs } from "../../words/words";
|
||||
import {
|
||||
isUnisexNoun,
|
||||
isLocativeAdverbEntry,
|
||||
isUnisexNounEntry,
|
||||
} from "../../lib/type-predicates";
|
||||
import { sort } from "./explorer-helpers";
|
||||
import {
|
||||
ParticipleInput,
|
||||
} from "../../lib/equative-machine";
|
||||
|
||||
const unisexNouns = sort(nouns.filter(x => isUnisexNoun(x)) as UnisexNoun[]);
|
||||
const nonUnisexNouns = sort(nouns.filter(x => !isUnisexNoun(x)) as (MascNoun | FemNoun)[]);
|
||||
function sort<T extends (AdjectiveEntry | NounEntry | VerbEntry | 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 => isUnisexNounEntry(x)) as UnisexNounEntry[]);
|
||||
const nonUnisexNouns = sort(nouns.filter(x => !isUnisexNounEntry(x)) as (MascNounEntry | FemNounEntry)[]);
|
||||
|
||||
const inputs = {
|
||||
adjective: sort(adjectives),
|
||||
unisexNoun: unisexNouns,
|
||||
noun: nonUnisexNouns,
|
||||
// @ts-ignore
|
||||
participle: sort(verbs.map(e => e.entry) as ParticipleInput[]),
|
||||
participle: sort(verbs),
|
||||
adverb: sort(adverbs.filter(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.p === "لیکل") || inputs.participle[0];
|
||||
export const defaultParticiple = inputs.participle.find(ps => ps.entry.p === "لیکل") || inputs.participle[0];
|
||||
|
||||
export default inputs;
|
|
@ -3,7 +3,7 @@ import { ExplorerState, ExplorerReducerAction } from "./explorer-types";
|
|||
|
||||
export function reducer(state: ExplorerState, action: ExplorerReducerAction): ExplorerState {
|
||||
if (action.type === "setPredicate") {
|
||||
const pile = inputs[state.predicate.type] as (UnisexNoun | Adjective)[];
|
||||
const pile = inputs[state.predicate.type] as (UnisexNounEntry | AdjectiveEntry)[];
|
||||
const predicate = (pile.find(p => p.ts === action.payload) || pile[0]);
|
||||
return {
|
||||
...state,
|
||||
|
@ -19,7 +19,7 @@ export function reducer(state: ExplorerState, action: ExplorerReducerAction): Ex
|
|||
...state,
|
||||
predicate: {
|
||||
...state.predicate,
|
||||
type: (predicateType === "unisexNoun" && state.subject.type === "noun") ? "adjective" : predicateType,
|
||||
type: predicateType,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -27,14 +27,17 @@ export function reducer(state: ExplorerState, action: ExplorerReducerAction): Ex
|
|||
const subjectType = action.payload;
|
||||
return {
|
||||
...state,
|
||||
predicate: {
|
||||
...state.predicate,
|
||||
type: state.predicate.type === "unisexNoun" ? "adjective" : state.predicate.type,
|
||||
},
|
||||
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") {
|
||||
|
@ -50,26 +53,28 @@ export function reducer(state: ExplorerState, action: ExplorerReducerAction): Ex
|
|||
},
|
||||
};
|
||||
}
|
||||
if (action.type === "setSubjectPlural") {
|
||||
if (action.type === "setNumber") {
|
||||
const entity = action.payload.entity;
|
||||
return {
|
||||
...state,
|
||||
subject: {
|
||||
...state.subject,
|
||||
[entity]: {
|
||||
...state[entity],
|
||||
info: {
|
||||
...state.subject.info,
|
||||
plural: action.payload,
|
||||
...state[entity].info,
|
||||
number: action.payload.number,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
if (action.type === "setSubjectGender") {
|
||||
if (action.type === "setGender") {
|
||||
const entity = action.payload.entity;
|
||||
return {
|
||||
...state,
|
||||
subject: {
|
||||
...state.subject,
|
||||
[entity]: {
|
||||
...state[entity],
|
||||
info: {
|
||||
...state.subject.info,
|
||||
gender: action.payload,
|
||||
...state[entity].info,
|
||||
gender: action.payload.gender,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -80,6 +85,15 @@ export function reducer(state: ExplorerState, action: ExplorerReducerAction): Ex
|
|||
tense: action.payload,
|
||||
};
|
||||
}
|
||||
// if (action.type === "setPredicateEntity") {
|
||||
// return {
|
||||
// ...state,
|
||||
// predicate: {
|
||||
// ...state.predicate,
|
||||
// entity: action.payload,
|
||||
// },
|
||||
// };
|
||||
// }
|
||||
return {
|
||||
...state,
|
||||
length: action.payload,
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
import { makeOptionLabel } from "./explorer-helpers";
|
||||
// import { makeOptionLabel } from "./explorer-helpers";
|
||||
import inputs from "./explorer-inputs";
|
||||
import {
|
||||
ExplorerReducerAction,
|
||||
ExplorerState,
|
||||
PredicateType,
|
||||
SubjectType,
|
||||
PredicateType,
|
||||
} from "./explorer-types";
|
||||
import {
|
||||
getEnglishParticiple,
|
||||
} from "../../lib/np-tools";
|
||||
import {
|
||||
ButtonSelect,
|
||||
getEnglishWord,
|
||||
Types as T,
|
||||
removeFVarients,
|
||||
} from "@lingdocs/pashto-inflector";
|
||||
import { isFemNoun, isMascNoun, isPluralEntry } from "../../lib/type-predicates";
|
||||
import { isAdjectiveEntry, isAdverbEntry, isFemNounEntry, isMascNounEntry, isNounEntry, isPluralNounEntry } from "../../lib/type-predicates";
|
||||
import Select from "react-select";
|
||||
|
||||
const zIndexProps = {
|
||||
|
@ -18,194 +23,166 @@ const zIndexProps = {
|
|||
styles: { menuPortal: (base: any) => ({ ...base, zIndex: 9999 }) },
|
||||
};
|
||||
|
||||
export function SubjectSelector({ state, dispatch }: {
|
||||
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>) {
|
||||
const t = e.target.value as SubjectType;
|
||||
dispatch({ type: "setSubjectType", payload: t });
|
||||
}
|
||||
function onSubjectSelect({ value }: any) {
|
||||
dispatch({ type: "setSubject", payload: parseInt(value) });
|
||||
}
|
||||
const pluralNounSelected = (
|
||||
state.subject.type === "noun" && isPluralEntry(state.subject.noun)
|
||||
);
|
||||
const options = state.subject.type === "pronouns"
|
||||
? []
|
||||
: inputs[state.subject.type].map(e => ({
|
||||
value: e.ts.toString(),
|
||||
label: makeOptionLabel(e),
|
||||
}));
|
||||
const subject = state.subject.type === "pronouns"
|
||||
? undefined
|
||||
: state.subject[state.subject.type];
|
||||
return <div className="form-group">
|
||||
<label htmlFor="predicate-select"><h5 className="mb-0">Subject:</h5></label>
|
||||
<div className="form-check">
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="radio"
|
||||
name="pronounsSubjectRadio"
|
||||
id="pronounsSubjectRadio"
|
||||
value="pronouns"
|
||||
checked={state.subject.type === "pronouns"}
|
||||
onChange={onTypeSelect}
|
||||
/>
|
||||
<label className="form-check-label" htmlFor="adjectivesPredicateRadio">
|
||||
Pronouns
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-check">
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="radio"
|
||||
name="nounsSubjectRadio"
|
||||
id="nounsSubjectRadio"
|
||||
value="noun"
|
||||
checked={state.subject.type === "noun"}
|
||||
onChange={onTypeSelect}
|
||||
/>
|
||||
<label className="form-check-label" htmlFor="unisexNounsPredicateRadio">
|
||||
Nouns
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-check">
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="radio"
|
||||
name="unisexNounsSubjectRadio"
|
||||
id="unisexNounsSubjectRadio"
|
||||
value="unisexNoun"
|
||||
checked={state.subject.type === "unisexNoun"}
|
||||
onChange={onTypeSelect}
|
||||
/>
|
||||
<label className="form-check-label" htmlFor="unisexNounsPredicateRadio">
|
||||
Unisex Nouns
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-check mb-2">
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="radio"
|
||||
name="participlesSubjectRadio"
|
||||
id="participlesSubjectRadio"
|
||||
value="participle"
|
||||
checked={state.subject.type === "participle"}
|
||||
onChange={onTypeSelect}
|
||||
/>
|
||||
<label className="form-check-label" htmlFor="unisexNounsPredicateRadio">
|
||||
Participles
|
||||
</label>
|
||||
</div>
|
||||
{state.subject.type !== "pronouns" &&
|
||||
<>
|
||||
<Select
|
||||
value={subject?.ts.toString()}
|
||||
onChange={onSubjectSelect}
|
||||
className="mb-2"
|
||||
// @ts-ignore
|
||||
options={options}
|
||||
isSearchable
|
||||
placeholder={options.find(o => o.value === subject?.ts.toString())?.label}
|
||||
{...zIndexProps}
|
||||
/>
|
||||
<div className="d-flex flex-row justify-content-center mt-3">
|
||||
<div className="mr-2">
|
||||
<ButtonSelect
|
||||
small
|
||||
options={[
|
||||
...(state.subject.type === "unisexNoun" || (state.subject.type === "participle") || (isMascNoun(state.subject[state.subject.type])))
|
||||
? [{ label: "Masc.", value: "masc" }] : [],
|
||||
...(state.subject.type === "unisexNoun" || ((state.subject.type !== "participle") && isFemNoun(state.subject[state.subject.type])))
|
||||
? [{ label: "Fem.", value: "fem" }] : [],
|
||||
]}
|
||||
value={state.subject.type === "noun"
|
||||
? (isMascNoun(state.subject[state.subject.type]) ? "masc" : "fem")
|
||||
: state.subject.type === "participle"
|
||||
? "masc"
|
||||
: state.subject.info.gender}
|
||||
handleChange={state.subject.type === "noun" ? p => null : (p) => dispatch({ type: "setSubjectGender", payload: p as T.Gender })}
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-2">
|
||||
<ButtonSelect
|
||||
small
|
||||
options={[
|
||||
...(!pluralNounSelected && state.subject.type !== "participle") ? [{ label: "Singular", value: "singular" }] : [],
|
||||
{ label: "Plural", value: "plural" },
|
||||
]}
|
||||
value={(state.subject.info.plural || pluralNounSelected || state.subject.type === "participle") ? "plural" : "singular"}
|
||||
handleChange={(p) => dispatch({ type: "setSubjectPlural", payload: p === "plural" ? true : false })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
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: NounEntry | VerbEntry | AdjectiveEntry | 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>;
|
||||
}
|
||||
|
||||
export function PredicateSelector({ state, dispatch }: {
|
||||
function GenderAndNumberSelect({ state, dispatch, entity }: {
|
||||
state: ExplorerState,
|
||||
dispatch: (action: ExplorerReducerAction) => void,
|
||||
entity: "subject" | "predicate",
|
||||
}) {
|
||||
function onTypeSelect(e: React.ChangeEvent<HTMLInputElement>) {
|
||||
const t = e.target.value as PredicateType;
|
||||
dispatch({ type: "setPredicateType", payload: t });
|
||||
const type = state[entity].type;
|
||||
if (type === "pronouns") {
|
||||
return <div>ERROR: Should not display with pronouns</div>;
|
||||
}
|
||||
function onPredicateSelect({ value }: any) {
|
||||
dispatch({ type: "setPredicate", payload: parseInt(value) });
|
||||
}
|
||||
const options = inputs[state.predicate.type].map(e => ({
|
||||
value: `${e.ts}`,
|
||||
label: makeOptionLabel(e),
|
||||
}));
|
||||
const predicate = state.predicate[state.predicate.type];
|
||||
return <div>
|
||||
<label htmlFor="predicate-select"><h5 className="mb-0">Predicate:</h5></label>
|
||||
<div className="form-check">
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="radio"
|
||||
name="adjectivesPredicateRadio"
|
||||
id="adjectivesPredicateRadio"
|
||||
value="adjective"
|
||||
checked={state.predicate.type === "adjective"}
|
||||
onChange={onTypeSelect}
|
||||
// @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 }})}
|
||||
/>
|
||||
<label className="form-check-label" htmlFor="adjectivesPredicateRadio">
|
||||
Adjectives
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-check mb-2">
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="radio"
|
||||
name="unisexNounsPredicateRadio"
|
||||
id="unisexNounsPredicateRadio"
|
||||
value="unisexNoun"
|
||||
checked={state.predicate.type === "unisexNoun"}
|
||||
onChange={onTypeSelect}
|
||||
disabled={state.subject.type !== "pronouns"}
|
||||
<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 }})}
|
||||
/>
|
||||
<label className="form-check-label" htmlFor="unisexNounsPredicateRadio">
|
||||
Unisex Nouns
|
||||
</label>
|
||||
</div>
|
||||
<Select
|
||||
value={predicate.ts.toString()}
|
||||
onChange={onPredicateSelect}
|
||||
className="mb-2"
|
||||
// @ts-ignore
|
||||
options={options}
|
||||
isSearchable
|
||||
placeholder={options.find(o => o.value === predicate.ts.toString())?.label}
|
||||
{...zIndexProps}
|
||||
/>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function makeOption(e: VerbEntry | NounEntry | AdjectiveEntry | 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,
|
||||
|
@ -222,7 +199,7 @@ export function TenseSelector({ state, dispatch }: {
|
|||
dispatch({ type: "setTense", payload: value });
|
||||
}
|
||||
return <div>
|
||||
<h5>Tense:</h5>
|
||||
<h5>Equative:</h5>
|
||||
<Select
|
||||
value={state.tense}
|
||||
onChange={onTenseSelect}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { Types as T } from "@lingdocs/pashto-inflector";
|
||||
import { ParticipleInput } from "../../lib/equative-machine";
|
||||
|
||||
export type PredicateType = "adjective" | "noun" | "unisexNoun" | "participle";
|
||||
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 = {
|
||||
|
@ -13,14 +14,18 @@ export type ExplorerState = {
|
|||
|
||||
export type SubjectEntityInfo = EntitiyInfo & { type: SubjectType };
|
||||
|
||||
export type PredicateEntityInfo = EntitiyInfo & { type: PredicateType, adjective: Adjective };
|
||||
export type PredicateEntityInfo = EntitiyInfo & {
|
||||
type: PredicateType,
|
||||
adjective: AdjectiveEntry,
|
||||
adverb: LocativeAdverbEntry,
|
||||
}
|
||||
|
||||
type EntitiyInfo = {
|
||||
noun: Noun,
|
||||
participle: ParticipleInput,
|
||||
unisexNoun: UnisexNoun,
|
||||
noun: NounEntry,
|
||||
participle: VerbEntry,
|
||||
unisexNoun: UnisexNounEntry,
|
||||
info: {
|
||||
plural: boolean,
|
||||
number: NounNumber,
|
||||
gender: T.Gender,
|
||||
},
|
||||
};
|
||||
|
@ -34,9 +39,9 @@ export type ExplorerReducerAction = {
|
|||
} | {
|
||||
type: "setSubject", payload: number,
|
||||
} | {
|
||||
type: "setSubjectPlural", payload: boolean,
|
||||
type: "setNumber", payload: { entity: "subject" | "predicate", number: NounNumber },
|
||||
} | {
|
||||
type: "setSubjectGender", payload: T.Gender,
|
||||
type: "setGender", payload: { entity: "subject" | "predicate", gender: T.Gender },
|
||||
} | {
|
||||
type: "setTense", payload: EquativeTense,
|
||||
} | {
|
||||
|
|
|
@ -4,4 +4,14 @@ title: Equative Explorer 🌎
|
|||
|
||||
import EquativeExplorer from "../../components/equative-explorer/EquativeExplorer";
|
||||
|
||||
<EquativeExplorer />
|
||||
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! 🤪
|
||||
|
||||
<div className="mb-4">
|
||||
<EquativeExplorer />
|
||||
</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.
|
||||
|
|
|
@ -30,12 +30,11 @@ import Link from "../../components/Link";
|
|||
import Table from "../../components/Table";
|
||||
import { startingWord } from "../../lib/starting-word";
|
||||
import {
|
||||
isFemNoun,
|
||||
isPattern6FemNoun,
|
||||
isPattern7FemNoun,
|
||||
isFemNounEntry,
|
||||
isPattern6FemEntry,
|
||||
} from "../../lib/type-predicates";
|
||||
|
||||
export const femNouns = nouns.filter(isFemNoun);
|
||||
export const femNouns = nouns.filter(isFemNounEntry);
|
||||
|
||||
The <Link to="/inflection/inflection-patterns/">5 basic patterns in the last chapter</Link> work with both masculine and feminine words (nouns and adjectives).
|
||||
|
||||
|
@ -43,6 +42,6 @@ There are also a few more patterns that are only for **feminine nouns**.
|
|||
|
||||
## Feminine Nouns Ending in <InlinePs opts={opts} ps={{ p: "ي", f: "ee" }} />
|
||||
|
||||
<InflectionCarousel items={startingWord(femNouns.filter(isPattern6FemNoun), "آزادي")} />
|
||||
<InflectionCarousel items={startingWord(femNouns.filter(isPattern6FemEntry), "آزادي")} />
|
||||
|
||||
**Note:** This only works with **inanimate nouns**. (ie. words for people like <InlinePs opts={opts} ps={{ p: "باجي", f: "baajée", e: "older sister" }} /> will not inflect like this.)
|
||||
|
|
|
@ -24,12 +24,12 @@ import {
|
|||
} from "@lingdocs/pashto-inflector";
|
||||
import {
|
||||
isNounOrVerb,
|
||||
isPattern1Word,
|
||||
isPattern2Word,
|
||||
isPattern3Word,
|
||||
isPattern4Word,
|
||||
isPattern5Word,
|
||||
isUnisexNoun,
|
||||
isPattern1Entry,
|
||||
isPattern2Entry,
|
||||
isPattern3Entry,
|
||||
isPattern4Entry,
|
||||
isPattern5Entry,
|
||||
isUnisexNounEntry,
|
||||
} from "../../lib/type-predicates";
|
||||
import InflectionCarousel from "../../components/InflectionCarousel";
|
||||
import { nouns, adjectives } from "../../words/words";
|
||||
|
@ -37,7 +37,7 @@ import { startingWord } from "../../lib/starting-word";
|
|||
import Link from "../../components/Link";
|
||||
|
||||
export const words = [
|
||||
...nouns.filter(isUnisexNoun),
|
||||
...nouns.filter(isUnisexNounEntry),
|
||||
...adjectives,
|
||||
];
|
||||
|
||||
|
@ -57,7 +57,7 @@ These words always end in:
|
|||
- **Feminine:** - <InlinePs opts={opts} ps={{ p: "ـه", f: "-a" }} />
|
||||
|
||||
<InflectionCarousel items={startingWord(
|
||||
words.filter(isPattern1Word),
|
||||
words.filter(isPattern1Entry),
|
||||
"غټ",
|
||||
)} />
|
||||
|
||||
|
@ -92,7 +92,7 @@ Notice how it does not use the first feminine inflection <InlinePs opts={opts} p
|
|||
## 2. Words ending in an unstressed <InlinePs opts={opts} ps={{ p: "ی", f: "ey" }} />
|
||||
|
||||
<InflectionCarousel items={startingWord(
|
||||
words.filter(isPattern2Word),
|
||||
words.filter(isPattern2Entry),
|
||||
"ستړی",
|
||||
)} />
|
||||
|
||||
|
@ -101,7 +101,7 @@ Notice how it does not use the first feminine inflection <InlinePs opts={opts} p
|
|||
This is very similar to pattern #2, but with the stress on the last syllable the feminine inflection changes.
|
||||
|
||||
<InflectionCarousel items={startingWord(
|
||||
words.filter(isPattern3Word),
|
||||
words.filter(isPattern3Entry),
|
||||
"لومړی",
|
||||
)} />
|
||||
|
||||
|
@ -113,7 +113,7 @@ These words are a little irregular but you can see a common patten based around:
|
|||
- shortening the other forms and adding the <InlinePs opts={opts} ps={{ p: "ـه", f: "-a" }} />, <InlinePs opts={opts} ps={{ p: "ـې", f: "-e" }} />, <InlinePs opts={opts} ps={{ p: "ـو", f: "-o" }} /> endings
|
||||
|
||||
<InflectionCarousel items={startingWord(
|
||||
words.filter(isPattern4Word),
|
||||
words.filter(isPattern4Entry),
|
||||
"پښتون",
|
||||
)} />
|
||||
|
||||
|
@ -124,7 +124,7 @@ These words are a little irregular but you can see a common patten based around:
|
|||
These are also a little irregular but instead of lengthening the 1st masculine inflection they compress it as well and take just an <InlinePs opts={opts} ps={{ p: "ـه", f: "-u" }} /> on the end.
|
||||
|
||||
<InflectionCarousel items={startingWord(
|
||||
words.filter(isPattern5Word),
|
||||
words.filter(isPattern5Entry),
|
||||
"غل",
|
||||
)} />
|
||||
|
||||
|
|
|
@ -18,38 +18,38 @@ import {
|
|||
firstVariation,
|
||||
} from "../../lib/text-tools";
|
||||
import {
|
||||
isMascNoun,
|
||||
isFemNoun,
|
||||
isUnisexNoun,
|
||||
isMascNounEntry,
|
||||
isFemNounEntry,
|
||||
isUnisexNounEntry,
|
||||
} from "../../lib/type-predicates";
|
||||
import { categorize } from "../../lib/categorize";
|
||||
|
||||
const genders: T.Gender[] = ["masc", "fem"];
|
||||
|
||||
const mascNouns = nouns.filter(isMascNoun);
|
||||
const mascNouns = nouns.filter(isMascNounEntry);
|
||||
const femNouns = [
|
||||
...nouns.filter(isFemNoun),
|
||||
...getFemVersions(mascNouns.filter(isUnisexNoun)),
|
||||
...nouns.filter(isFemNounEntry),
|
||||
...getFemVersions(mascNouns.filter(isUnisexNounEntry)),
|
||||
];
|
||||
|
||||
const types = {
|
||||
masc: categorize<MascNoun, {
|
||||
consonantMasc: MascNoun[],
|
||||
eyMasc: MascNoun[],
|
||||
uMasc: MascNoun[],
|
||||
yMasc: MascNoun[],
|
||||
masc: categorize<MascNounEntry, {
|
||||
consonantMasc: MascNounEntry[],
|
||||
eyMasc: MascNounEntry[],
|
||||
uMasc: MascNounEntry[],
|
||||
yMasc: MascNounEntry[],
|
||||
}>(mascNouns, {
|
||||
consonantMasc: endsWith([{ p: pashtoConsonants }, { p: "و", f: "w" }]),
|
||||
eyMasc: endsWith({ p: "ی", f: "ey" }),
|
||||
uMasc: endsWith({ p: "ه", f: "u" }),
|
||||
yMasc: endsWith([{ p: "ای", f: "aay" }, { p: "وی", f: "ooy" }]),
|
||||
}),
|
||||
fem: categorize<FemNoun, {
|
||||
aaFem: FemNoun[],
|
||||
eeFem: FemNoun[],
|
||||
uyFem: FemNoun[],
|
||||
aFem: FemNoun[],
|
||||
eFem: FemNoun[],
|
||||
fem: categorize<FemNounEntry, {
|
||||
aaFem: FemNounEntry[],
|
||||
eeFem: FemNounEntry[],
|
||||
uyFem: FemNounEntry[],
|
||||
aFem: FemNounEntry[],
|
||||
eFem: FemNounEntry[],
|
||||
}>(femNouns, {
|
||||
aaFem: endsWith({ p: "ا", f: "aa" }),
|
||||
eeFem: endsWith({ p: "ي", f: "ee" }),
|
||||
|
@ -59,7 +59,7 @@ const types = {
|
|||
}),
|
||||
};
|
||||
|
||||
function getFemVersions(uns: UnisexNoun[]): FemNoun[] {
|
||||
function getFemVersions(uns: UnisexNounEntry[]): FemNounEntry[] {
|
||||
return uns.map((n) => {
|
||||
const infs = inflectWord(n);
|
||||
if (!infs || !infs.inflections) return undefined;
|
||||
|
@ -68,18 +68,18 @@ function getFemVersions(uns: UnisexNoun[]): FemNoun[] {
|
|||
e: n.e,
|
||||
...infs.inflections.fem[0][0],
|
||||
} as T.DictionaryEntry;
|
||||
}).filter(n => !!n) as FemNoun[];
|
||||
}).filter(n => !!n) as FemNounEntry[];
|
||||
}
|
||||
|
||||
function flatten<T>(o: Record<string, T[]>): T[] {
|
||||
return Object.values(o).flat();
|
||||
}
|
||||
|
||||
function nounNotIn(st: Noun[]): (n: Noun | T.DictionaryEntry) => boolean {
|
||||
function nounNotIn(st: NounEntry[]): (n: NounEntry | T.DictionaryEntry) => boolean {
|
||||
return (n: T.DictionaryEntry) => !st.find(x => x.ts === n.ts);
|
||||
}
|
||||
|
||||
type CategorySet = Record<string, Noun[]>;
|
||||
type CategorySet = Record<string, NounEntry[]>;
|
||||
// for some reason we need to use this CategorySet type here... 🤷♂️
|
||||
const exceptions: Record<string, CategorySet> = {
|
||||
masc: {
|
||||
|
|
|
@ -19,10 +19,10 @@ import {
|
|||
firstVariation,
|
||||
} from "../../lib/text-tools";
|
||||
import {
|
||||
isUnisexNoun,
|
||||
isUnisexNounEntry,
|
||||
} from "../../lib/type-predicates";
|
||||
|
||||
const unisexNouns = nouns.filter(isUnisexNoun);
|
||||
const unisexNouns = nouns.filter(isUnisexNounEntry);
|
||||
type NType = "pattern1" | "pattern2" | "pattern3" | "pattern4" | "pattern5" | "other";
|
||||
// TODO: make pattern types as overlay types
|
||||
const types = intoPatterns(unisexNouns);
|
||||
|
@ -41,7 +41,7 @@ export default function UnisexNounGame({ id, link }: { id: string, link: string
|
|||
do {
|
||||
type = getRandomFromList(keys);
|
||||
} while (!pool[type].length);
|
||||
const entry = getRandomFromList<UnisexNoun>(
|
||||
const entry = getRandomFromList<UnisexNounEntry>(
|
||||
// @ts-ignore
|
||||
pool[type]
|
||||
);
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import {
|
||||
isPattern1Word,
|
||||
isPattern2Word,
|
||||
isPattern3Word,
|
||||
isPattern4Word,
|
||||
isPattern5Word,
|
||||
isPattern1Entry,
|
||||
isPattern2Entry,
|
||||
isPattern3Entry,
|
||||
isPattern4Entry,
|
||||
isPattern5Entry,
|
||||
} from "./type-predicates";
|
||||
|
||||
/**
|
||||
|
@ -75,31 +75,31 @@ export function categorize<I, X extends Record<string, I[]>>(
|
|||
|
||||
// TODO: uncategorizable words like ایرې - n. f. pl. -- could be pattern 1 or 2 🤷♂️
|
||||
|
||||
export function intoPatterns<T extends (Noun | Adjective)>(words: T[]): {
|
||||
"pattern1": Pattern1Word<T>[],
|
||||
"pattern2": Pattern2Word<T>[],
|
||||
"pattern3": Pattern3Word<T>[],
|
||||
"pattern4": Pattern4Word<T>[],
|
||||
"pattern5": Pattern5Word<T>[],
|
||||
export function intoPatterns<T extends (NounEntry | AdjectiveEntry)>(words: T[]): {
|
||||
"pattern1": Pattern1Entry<T>[],
|
||||
"pattern2": Pattern2Entry<T>[],
|
||||
"pattern3": Pattern3Entry<T>[],
|
||||
"pattern4": Pattern4Entry<T>[],
|
||||
"pattern5": Pattern5Entry<T>[],
|
||||
"other": NonInflecting<T>[],
|
||||
// "pattern6fem": Pattern6FemNoun<T>[],
|
||||
} {
|
||||
return categorize<(Noun | Adjective), {
|
||||
"pattern1": Pattern1Word<T>[],
|
||||
"pattern2": Pattern2Word<T>[],
|
||||
"pattern3": Pattern3Word<T>[],
|
||||
"pattern4": Pattern4Word<T>[],
|
||||
"pattern5": Pattern5Word<T>[],
|
||||
return categorize<(NounEntry | AdjectiveEntry), {
|
||||
"pattern1": Pattern1Entry<T>[],
|
||||
"pattern2": Pattern2Entry<T>[],
|
||||
"pattern3": Pattern3Entry<T>[],
|
||||
"pattern4": Pattern4Entry<T>[],
|
||||
"pattern5": Pattern5Entry<T>[],
|
||||
"other": NonInflecting<T>[],
|
||||
// "pattern6fem": Pattern6FemNoun<T>[],
|
||||
}>(
|
||||
words,
|
||||
{
|
||||
"pattern1": isPattern1Word,
|
||||
"pattern2": isPattern2Word,
|
||||
"pattern3": isPattern3Word,
|
||||
"pattern4": isPattern4Word,
|
||||
"pattern5": isPattern5Word,
|
||||
"pattern1": isPattern1Entry,
|
||||
"pattern2": isPattern2Entry,
|
||||
"pattern3": isPattern3Entry,
|
||||
"pattern4": isPattern4Entry,
|
||||
"pattern5": isPattern5Entry,
|
||||
// "pattern6fem": (n) => (isNoun(n) && isPattern6FemNoun(n)),
|
||||
"other": "leftovers",
|
||||
},
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
import {
|
||||
Types as T,
|
||||
getEnglishWord,
|
||||
inflectWord,
|
||||
isUnisexSet,
|
||||
personGender,
|
||||
personIsPlural,
|
||||
} from "@lingdocs/pashto-inflector";
|
||||
import { isAdjectiveEntry, isLocativeAdverbEntry } from "./type-predicates";
|
||||
import {
|
||||
psStringFromEntry,
|
||||
} from "./text-tools";
|
||||
|
||||
export function evaluateCompliment(c: Compliment, person: T.Person): { ps: T.PsString[], e: string } {
|
||||
const e = getEnglishWord(c.entry);
|
||||
if (!e || typeof e !== "string") {
|
||||
console.log(e);
|
||||
throw new Error("error getting english for compliment");
|
||||
}
|
||||
if (isLocativeAdverbEntry(c.entry)) {
|
||||
return {
|
||||
ps: [psStringFromEntry(c.entry)],
|
||||
e,
|
||||
};
|
||||
}
|
||||
if (isAdjectiveEntry(c.entry)) {
|
||||
const infs = inflectWord(c.entry);
|
||||
if (!infs) return {
|
||||
ps: [psStringFromEntry(c.entry)],
|
||||
e,
|
||||
}
|
||||
if (!infs.inflections || !isUnisexSet(infs.inflections)) {
|
||||
throw new Error("error getting inflections for adjective, looks like a noun's inflections");
|
||||
}
|
||||
return {
|
||||
ps: chooseInflection(infs.inflections, person),
|
||||
e,
|
||||
};
|
||||
}
|
||||
throw new Error("noun complements not yet implemented");
|
||||
}
|
||||
|
||||
function chooseInflection(inflections: T.UnisexSet<T.InflectionSet>, pers: T.Person): T.ArrayOneOrMore<T.PsString> {
|
||||
return inflections[personGender(pers)][personIsPlural(pers) ? 1 : 0];
|
||||
}
|
|
@ -1,356 +1,89 @@
|
|||
import {
|
||||
getPersonFromVerbForm,
|
||||
Types as T,
|
||||
getEnglishWord,
|
||||
grammarUnits,
|
||||
inflectWord,
|
||||
personIsPlural,
|
||||
removeFVarients,
|
||||
isUnisexSet,
|
||||
personGender,
|
||||
getVerbBlockPosFromPerson,
|
||||
addEnglish,
|
||||
parseEc,
|
||||
getPersonFromVerbForm,
|
||||
concatPsString,
|
||||
} from "@lingdocs/pashto-inflector";
|
||||
import {
|
||||
isArrayOneOrMore, isPluralEntry,
|
||||
} from "./type-predicates";
|
||||
personFromNP,
|
||||
evaluateNP,
|
||||
} from "./np-tools";
|
||||
import {
|
||||
evaluateCompliment,
|
||||
} from "./compliment-tools";
|
||||
|
||||
export type EquativeMachineOutput = {
|
||||
subject: T.PsString[],
|
||||
predicate: T.PsString[],
|
||||
equative: T.SingleOrLengthOpts<T.ArrayOneOrMore<T.PsString>>,
|
||||
ba: boolean,
|
||||
};
|
||||
// Equative Rules
|
||||
// - An equative equates a SUBJECT: Noun Phrase and a PREDICATE: Noun Phrase | Compliment
|
||||
// - In Pashto, the equative agrees with the predicate when the predicate is a Noun Phrase,
|
||||
// otherwise it agrees with the subject
|
||||
// - If the subject is a pronoun, always agree with the subject
|
||||
// - In English, the equative agrees with the subject
|
||||
|
||||
export type NounInput = {
|
||||
entry: SingularEntry<Noun>,
|
||||
plural: boolean,
|
||||
} | {
|
||||
entry: PluralEntry<Noun>,
|
||||
plural: true,
|
||||
};
|
||||
|
||||
export type ParticipleInput = T.DictionaryEntry & { __brand: "a participle" };
|
||||
export type SpecifiedUnisexNounInput = NounInput & { gender: T.Gender };
|
||||
export type PersonInput = T.Person;
|
||||
|
||||
export type EntityInput = SubjectInput | PredicateInput;
|
||||
export type SubjectInput = PersonInput | NounInput | ParticipleInput | SpecifiedUnisexNounInput;
|
||||
export type PredicateInput = PersonInput | NounInput | Adjective | SpecifiedUnisexNounInput | UnisexNoun | ParticipleInput;
|
||||
|
||||
export function equativeMachine(sub: SubjectInput, pred: PredicateInput, tense: EquativeTense): EquativeMachineOutput {
|
||||
// - english equative always agrees with subject
|
||||
// - pashto equative agrees with predicate, unless it's an adjective, in which case the
|
||||
// agreement reverts to the subject
|
||||
const subjPerson = getInputPerson(sub, "subject");
|
||||
const predPerson = getInputPerson(pred, "predicate") || subjPerson;
|
||||
const subject = makeEntity(sub);
|
||||
const predicate = makeEntity(pred, subjPerson);
|
||||
const equative = makeEquative(subjPerson, predPerson, sub, tense);
|
||||
const ba = determineBa(tense);
|
||||
export function equativeMachine(e: EquativeClause): EquativeClauseOutput {
|
||||
const ba = (e.tense === "future" || e.tense === "wouldBe");
|
||||
const subject = evaluateNP(e.subject);
|
||||
const predicate = ("type" in e.predicate && e.predicate.type === "compliment")
|
||||
? evaluateCompliment(e.predicate, personFromNP(e.subject))
|
||||
: evaluateNP(e.predicate);
|
||||
const equative = makeEquative(e);
|
||||
return {
|
||||
ba,
|
||||
subject,
|
||||
predicate,
|
||||
equative,
|
||||
ba,
|
||||
};
|
||||
}
|
||||
|
||||
export function assembleEquativeOutput(o: EquativeMachineOutput): T.SingleOrLengthOpts<T.ArrayOneOrMore<T.PsString>> {
|
||||
if ("long" in o.equative) {
|
||||
export function assembleEquativeOutput(o: EquativeClauseOutput): T.SingleOrLengthOpts<T.ArrayOneOrMore<T.PsString>> {
|
||||
if ("long" in o.equative.ps) {
|
||||
return {
|
||||
long: assembleEquativeOutput({ ...o, equative: o.equative.long }) as T.ArrayOneOrMore<T.PsString>,
|
||||
short: assembleEquativeOutput({ ...o, equative: o.equative.short }) as T.ArrayOneOrMore<T.PsString>,
|
||||
long: assembleEquativeOutput({ ...o, equative: { ...o.equative, ps: o.equative.ps.long }}) as T.ArrayOneOrMore<T.PsString>,
|
||||
short: assembleEquativeOutput({ ...o, equative: { ...o.equative, ps:o.equative.ps.short }}) as T.ArrayOneOrMore<T.PsString>,
|
||||
}
|
||||
}
|
||||
// get all possible combinations of subject, predicate, and equative
|
||||
// soooo cool how this works 🤓
|
||||
const equatives = o.equative;
|
||||
const predicates = o.predicate;
|
||||
const equatives = o.equative.ps;
|
||||
const predicates = o.predicate.ps;
|
||||
const ba = o.ba ? { p: " به", f: " ba" } : "";
|
||||
const ps = o.subject.flatMap(subj => (
|
||||
const ps = o.subject.ps.flatMap(subj => (
|
||||
predicates.flatMap(pred => (
|
||||
equatives.map(eq => (
|
||||
concatPsString(subj, ba, " ", pred, " ", eq))
|
||||
)
|
||||
))
|
||||
));
|
||||
const e = `${o.subject[0].e} ${o.equative[0].e} ${o.predicate[0].e}`;
|
||||
const e = `${o.subject.e} ${o.equative.e[0]} ${o.predicate.e}`;
|
||||
return ps.map(x => ({ ...x, e })) as T.ArrayOneOrMore<T.PsString>;
|
||||
}
|
||||
|
||||
// LEVEL 2 FUNCTIONS
|
||||
|
||||
function determineBa(tense: EquativeTense): boolean {
|
||||
return (tense === "future" || tense === "wouldBe");
|
||||
}
|
||||
|
||||
function getInputPerson(e: SubjectInput, part: "subject"): T.Person;
|
||||
function getInputPerson(e: PredicateInput, part: "predicate"): T.Person | undefined;
|
||||
function getInputPerson(e: EntityInput, part: "subject" | "predicate"): T.Person | undefined {
|
||||
function nounPerson(gender: T.Gender, plural: boolean): T.Person {
|
||||
return plural
|
||||
? ((gender === "masc") ? T.Person.ThirdPlurMale : T.Person.ThirdPlurFemale)
|
||||
: ((gender === "masc") ? T.Person.ThirdSingMale : T.Person.ThirdSingFemale);
|
||||
}
|
||||
|
||||
if (isPersonInput(e)) return e;
|
||||
if (isNounInput(e)) {
|
||||
const gender = e.entry.c?.includes("n. m.") ? "masc" : "fem"
|
||||
return nounPerson(gender, e.plural);
|
||||
}
|
||||
if (isSpecifiedUnisexNounInput(e)) {
|
||||
return nounPerson(e.gender, e.plural);
|
||||
}
|
||||
if (isParticipleInput(e)) return T.Person.ThirdPlurMale;
|
||||
if (isUnisexNounInput(e)) return undefined;
|
||||
if (isAdjectiveInput(e)) return undefined;
|
||||
}
|
||||
|
||||
function makeEntity(e: EntityInput, subjPerson?: T.Person): T.PsString[] {
|
||||
const isSubject = subjPerson === undefined;
|
||||
if (typeof e === "number") return makePronoun(e);
|
||||
if ("entry" in e) {
|
||||
return makeNoun(e, isSubject ? "subject" : "predicate");
|
||||
}
|
||||
if (isAdjectiveInput(e) && subjPerson !== undefined) {
|
||||
return makeAdjective(e, subjPerson);
|
||||
}
|
||||
if (isUnisexNounInput(e)) {
|
||||
if (subjPerson === undefined) throw new Error("unspecified unisex noun must be in the predicate");
|
||||
return makeUnisexNoun(e, subjPerson);
|
||||
}
|
||||
if (isParticipleInput(e)) {
|
||||
return makeParticiple(e);
|
||||
}
|
||||
throw new Error(`invalid entity in ${subjPerson ? "predicate" : "subject"}`);
|
||||
}
|
||||
|
||||
function makeEquative(subj: T.Person, pred: T.Person, subjectInput: SubjectInput, tense: EquativeTense): T.SentenceForm {
|
||||
function getEngEq(row: number, col: number): string {
|
||||
const t = grammarUnits.englishEquative[tense === "subjunctive" ? "present" : tense];
|
||||
function makeEquative(e: EquativeClause) {
|
||||
function getEngEq(row: number, col: number): string[] {
|
||||
const t = grammarUnits.englishEquative[e.tense === "subjunctive" ? "present" : e.tense];
|
||||
return typeof t === "string"
|
||||
? t
|
||||
: t[row][col];
|
||||
? [t]
|
||||
: [t[row][col]];
|
||||
}
|
||||
|
||||
const isPluralNoun = isNounInput(subjectInput) && isPluralEntry(subjectInput.entry);
|
||||
// The subject's person information, for the English equative
|
||||
const [eeRow, eeCol] = getVerbBlockPosFromPerson(
|
||||
(isParticipleInput(subjectInput) || isPluralNoun)
|
||||
? T.Person.ThirdSingMale
|
||||
: subj
|
||||
);
|
||||
const baseTense = (tense === "future")
|
||||
const baseTense = (e.tense === "future")
|
||||
? "subjunctive"
|
||||
: tense === "wouldBe"
|
||||
: e.tense === "wouldBe"
|
||||
? "past"
|
||||
: tense;
|
||||
return addEnglish(
|
||||
getEngEq(eeRow, eeCol),
|
||||
// pashto agrees with predicate (if possible)
|
||||
getPersonFromVerbForm(grammarUnits.equativeEndings[baseTense], pred),
|
||||
);
|
||||
}
|
||||
|
||||
// LEVEL 3 FUNCTIONS
|
||||
|
||||
function makePronoun(sub: T.Person): T.PsString[] {
|
||||
const [row, col] = getVerbBlockPosFromPerson(sub);
|
||||
return addEnglish(
|
||||
grammarUnits.persons[sub].label.subject,
|
||||
grammarUnits.pronouns.far.plain[row][col],
|
||||
);
|
||||
}
|
||||
|
||||
function makeUnisexNoun(e: UnisexNoun, subjPerson: T.Person): T.PsString[] {
|
||||
// reuse english from make noun - do the a / an sensitivity
|
||||
// if it's the predicate - get the inflection according to the subjPerson
|
||||
const inf = inflectWord(e);
|
||||
const english = getEnglishFromNoun(e, personIsPlural(subjPerson), "predicate");
|
||||
const gender = personGender(subjPerson);
|
||||
if (!inf) {
|
||||
return [psStringFromEntry(e, english)];
|
||||
}
|
||||
// if (!inf.inflections && (!("plural" in inf) || (!inf.inflections || !isUnisexSet(inf.inflections)))) {
|
||||
// throw Error("improper unisex noun");
|
||||
// }
|
||||
// if plural // anim // chose that
|
||||
// otherwise just chose inflection (or add both)
|
||||
const pashto = ((): T.ArrayOneOrMore<T.PsString> => {
|
||||
const plural = personIsPlural(subjPerson);
|
||||
function getPlural() {
|
||||
const plural = getInf(inf, "plural", gender, true);
|
||||
const arabicPlural = getInf(inf, "arabicPlural", gender, true);
|
||||
const inflections = getInf(inf, "inflections", gender, true)
|
||||
return [
|
||||
...plural,
|
||||
...arabicPlural,
|
||||
// avoid useless non-inflecting masculine inflection "plural"
|
||||
...(plural.length && inflections[0].p === e.p) ? [] : inflections,
|
||||
];
|
||||
}
|
||||
const ps = plural
|
||||
? getPlural()
|
||||
: getInf(inf, "inflections", gender, plural);
|
||||
return isArrayOneOrMore(ps)
|
||||
? ps
|
||||
: [psStringFromEntry(e, english)];
|
||||
})();
|
||||
return addEnglish(english, pashto);
|
||||
}
|
||||
|
||||
function getInf(infs: T.InflectorOutput, t: "plural" | "arabicPlural" | "inflections", gender: T.Gender, plural: boolean): T.PsString[] {
|
||||
// @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 ipick = iset[(t === "inflections" && plural) ? 1 : 0];
|
||||
return ipick;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function makeNoun(n: NounInput | SpecifiedUnisexNounInput, entity: "subject" | "predicate"): T.PsString[] {
|
||||
const english = getEnglishFromNoun(n.entry, n.plural, entity);
|
||||
|
||||
const pashto = ((): T.ArrayOneOrMore<T.PsString> => {
|
||||
const infs = inflectWord(n.entry);
|
||||
const gender = "gender" in n
|
||||
? n.gender
|
||||
: n.entry.c?.includes("n. f.") ? "fem" : "masc";
|
||||
const ps = !n.plural
|
||||
? getInf(infs, "inflections", gender, false)
|
||||
: [
|
||||
...getInf(infs, "plural", gender, true),
|
||||
...getInf(infs, "arabicPlural", gender, true),
|
||||
...getInf(infs, "inflections", gender, true),
|
||||
];
|
||||
return isArrayOneOrMore(ps)
|
||||
? ps
|
||||
: [psStringFromEntry(n.entry, english)];
|
||||
})();
|
||||
return addEnglish(english, pashto);
|
||||
}
|
||||
|
||||
function makeAdjective(e: Adjective, pers: T.Person): T.PsString[] {
|
||||
const inf = inflectWord(e);
|
||||
const english = getEnglishWord(e);
|
||||
if (!english) throw new Error("no english available for adjective");
|
||||
if (typeof english !== "string") throw new Error("error getting english for adjective, looks like a noun");
|
||||
// non-inflecting adjective
|
||||
if (!inf) return [psStringFromEntry(e, english)];
|
||||
if (!inf.inflections) throw new Error("error getting inflections, looks like a noun")
|
||||
if (!isUnisexSet(inf.inflections)) throw new Error("inflections for adjective were not unisex, looks like a noun");
|
||||
// inflecting adjective - inflected based on the subject person
|
||||
return addEnglish(
|
||||
english,
|
||||
chooseInflection(inf.inflections, pers),
|
||||
);
|
||||
}
|
||||
|
||||
function makeParticiple(e: T.DictionaryEntry): T.PsString[] {
|
||||
return [psStringFromEntry(e, getEnglishParticiple(e))];
|
||||
}
|
||||
|
||||
|
||||
// LEVEL 4 FUNCTIONS
|
||||
|
||||
function chooseInflection(inflections: T.UnisexSet<T.InflectionSet>, pers: T.Person): T.ArrayOneOrMore<T.PsString> {
|
||||
return inflections[personGender(pers)][personIsPlural(pers) ? 1 : 0];
|
||||
}
|
||||
|
||||
function getEnglishFromNoun(entry: T.DictionaryEntry, plural: boolean, entity: "subject" | "predicate"): string {
|
||||
const articles = {
|
||||
singular: "(A/The)",
|
||||
plural: "(The)",
|
||||
};
|
||||
const article = articles[plural ? "plural" : "singular"];
|
||||
function addArticle(s: string) {
|
||||
return `${entity === "subject" ? article : article.toLowerCase()} ${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 (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 getEnglishForUnisexNoun(pred: UnisexNounInput, pers: T.Person): string | undefined {
|
||||
// const english = getEnglishWord(pred);
|
||||
// const plurSing = personIsPlural(pers) ? "plural" : "singular";
|
||||
// return typeof english === "string"
|
||||
// ? english
|
||||
// : english === undefined
|
||||
// ? undefined
|
||||
// : english[plurSing]
|
||||
// ? english[plurSing]
|
||||
// : undefined;
|
||||
// }
|
||||
|
||||
function psStringFromEntry(entry: T.DictionaryEntry, e: string): T.PsString {
|
||||
: e.tense;
|
||||
const subjP = personFromNP(e.subject);
|
||||
const englishPerson = (e.subject.type === "plural noun" || e.subject.type === "participle")
|
||||
? T.Person.ThirdSingMale
|
||||
: subjP
|
||||
const pashtoPerson = (e.subject.type === "pronoun")
|
||||
? e.subject.person
|
||||
: ("type" in e.predicate && e.predicate.type === "compliment")
|
||||
? subjP
|
||||
: personFromNP(e.predicate);
|
||||
return {
|
||||
p: entry.p,
|
||||
f: removeFVarients(entry.f),
|
||||
e,
|
||||
ps: getPersonFromVerbForm(
|
||||
grammarUnits.equativeEndings[baseTense],
|
||||
pashtoPerson,
|
||||
),
|
||||
e: getEngEq(...getVerbBlockPosFromPerson(englishPerson)),
|
||||
};
|
||||
}
|
||||
|
||||
export function getEnglishParticiple(entry: T.DictionaryEntry): string {
|
||||
if (!entry.ec) throw new Error("no english information for participle");
|
||||
const ec = parseEc(entry.ec);
|
||||
const participle = ec[2];
|
||||
return (entry.ep)
|
||||
? `${participle} ${entry.ep}`
|
||||
: participle;
|
||||
}
|
||||
|
||||
export function isPersonInput(e: EntityInput | T.DictionaryEntry): e is PersonInput {
|
||||
return typeof e === "number";
|
||||
}
|
||||
|
||||
export function isNounInput(e: EntityInput | T.DictionaryEntry): e is NounInput {
|
||||
if (isPersonInput(e)) return false;
|
||||
if ("entry" in e && !("gender" in e)) {
|
||||
// e
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isParticipleInput(e: EntityInput | T.DictionaryEntry): e is ParticipleInput {
|
||||
if (isPersonInput(e)) return false;
|
||||
if ("entry" in e) return false;
|
||||
return !!e.c?.startsWith("v.");
|
||||
}
|
||||
|
||||
export function isSpecifiedUnisexNounInput(e: EntityInput): e is SpecifiedUnisexNounInput {
|
||||
if (isPersonInput(e)) return false;
|
||||
if ("entry" in e && "gender" in e) {
|
||||
// e
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isUnisexNounInput(e: EntityInput): e is UnisexNoun {
|
||||
if (isPersonInput(e)) return false;
|
||||
if ("entry" in e) return false;
|
||||
return !!e.c?.includes("unisex");
|
||||
}
|
||||
|
||||
export function isAdjectiveInput(e: EntityInput): e is Adjective {
|
||||
if (isPersonInput(e)) return false;
|
||||
if ("entry" in e) return false;
|
||||
if (isNounInput(e)) return false;
|
||||
if (isUnisexNounInput(e)) return false;
|
||||
if (isSpecifiedUnisexNounInput(e)) return false;
|
||||
return !!(e.c?.includes("adj.") && !(e.c.includes("n. m.") || e.c.includes("n. f.")));
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
import { isMascNounEntry } from "./type-predicates";
|
||||
import {
|
||||
Types as T,
|
||||
getEnglishWord,
|
||||
parseEc,
|
||||
getVerbBlockPosFromPerson,
|
||||
grammarUnits,
|
||||
inflectWord,
|
||||
} from "@lingdocs/pashto-inflector";
|
||||
import {
|
||||
psStringFromEntry,
|
||||
} from "./text-tools";
|
||||
|
||||
export function personFromNP(np: NounPhrase): T.Person {
|
||||
if (np.type === "participle") {
|
||||
return T.Person.ThirdPlurMale;
|
||||
}
|
||||
if (np.type === "pronoun") {
|
||||
return np.person;
|
||||
}
|
||||
const gender: T.Gender = "gender" in np
|
||||
? np.gender
|
||||
: isMascNounEntry(np.entry)
|
||||
? "masc"
|
||||
: "fem";
|
||||
const number: NounNumber = np.type === "plural noun"
|
||||
? "plural"
|
||||
: np.number;
|
||||
return number === "plural"
|
||||
? (gender === "masc" ? T.Person.ThirdPlurMale : T.Person.ThirdPlurFemale)
|
||||
: (gender === "masc" ? T.Person.ThirdSingMale : T.Person.ThirdSingFemale);
|
||||
}
|
||||
|
||||
export function evaluateNP(np: NounPhrase): { ps: T.PsString[], e: string } {
|
||||
if (np.type === "participle") {
|
||||
return evaluateParticiple(np);
|
||||
}
|
||||
if (np.type === "pronoun") {
|
||||
return evaluatePronoun(np);
|
||||
}
|
||||
return evaluateNoun(np);
|
||||
}
|
||||
|
||||
function evaluatePronoun(p: Pronoun): { ps: T.PsString[], e: string } {
|
||||
// TODO: Will need to handle inflecting and inflecting english pronouns etc.
|
||||
const [row, col] = getVerbBlockPosFromPerson(p.person);
|
||||
return {
|
||||
ps: grammarUnits.pronouns[p.pronounType].plain[row][col],
|
||||
e: grammarUnits.persons[p.person].label.subject,
|
||||
};
|
||||
}
|
||||
|
||||
function evaluateNoun(n: Noun): { ps: T.PsString[], e: string } {
|
||||
const number: NounNumber = "number" in n ? n.number : "plural";
|
||||
const english = getEnglishFromNoun(n.entry, number);
|
||||
const pashto = ((): T.PsString[] => {
|
||||
const infs = inflectWord(n.entry);
|
||||
const gender: T.Gender = "gender" in n ? n.gender :
|
||||
(isMascNounEntry(n.entry) ? "masc" : "fem");
|
||||
const ps = number === "singular"
|
||||
? getInf(infs, "inflections", gender, false)
|
||||
: [
|
||||
...getInf(infs, "plural", gender, true),
|
||||
...getInf(infs, "arabicPlural", gender, true),
|
||||
...getInf(infs, "inflections", gender, true),
|
||||
];
|
||||
return ps.length > 0
|
||||
? ps
|
||||
: [psStringFromEntry(n.entry)];
|
||||
})();
|
||||
return { ps: pashto, e: english };
|
||||
}
|
||||
|
||||
function getInf(infs: T.InflectorOutput, t: "plural" | "arabicPlural" | "inflections", gender: T.Gender, plural: boolean): T.PsString[] {
|
||||
// @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 ipick = iset[(t === "inflections" && plural) ? 1 : 0];
|
||||
return ipick;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
export 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 evaluateParticiple({ entry: { entry }}: Participle): { ps: T.PsString[], e: string } {
|
||||
return {
|
||||
ps: [psStringFromEntry(entry)],
|
||||
e: getEnglishParticiple(entry),
|
||||
};
|
||||
}
|
||||
|
||||
function getEnglishFromNoun(entry: T.DictionaryEntry, number: 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);
|
||||
}
|
||||
|
|
@ -248,7 +248,7 @@ const abilities: {
|
|||
{
|
||||
in: {
|
||||
subject: {
|
||||
entry: {"ts":1527815008,"i":8433,"p":"شودې","f":"shoodé","g":"shoode","e":"milk","c":"n. f. pl."} as PluralEntry<Noun>,
|
||||
entry: {"ts":1527815008,"i":8433,"p":"شودې","f":"shoodé","g":"shoode","e":"milk","c":"n. f. pl."} as PluralNounEntry<Noun>,
|
||||
plural: true,
|
||||
},
|
||||
predicate: {"ts":1527812796,"i":8578,"p":"ښه","f":"xu","g":"xu","e":"good","c":"adj."} as Adjective,
|
||||
|
@ -264,7 +264,7 @@ const abilities: {
|
|||
{
|
||||
in: {
|
||||
subject: {
|
||||
entry: {"ts":1527817330,"i":9204,"p":"غنم","f":"ghanúm","g":"ghanum","e":"wheat","c":"n. m. pl."} as PluralEntry<Noun>,
|
||||
entry: {"ts":1527817330,"i":9204,"p":"غنم","f":"ghanúm","g":"ghanum","e":"wheat","c":"n. m. pl."} as PluralNounEntry<Noun>,
|
||||
plural: true,
|
||||
},
|
||||
predicate: {"ts":1527815451,"i":7192,"p":"زوړ","f":"zoR","g":"zoR","e":"old","c":"adj. irreg.","infap":"زاړه","infaf":"zaaRu","infbp":"زړ","infbf":"zaR"} as Adjective,
|
|
@ -1,6 +1,6 @@
|
|||
import shuffle from "./shuffle-array";
|
||||
|
||||
export const startingWord = (words: Readonly<(Noun | Adjective)[]>, p: string) => {
|
||||
export const startingWord = (words: Readonly<(NounEntry | AdjectiveEntry)[]>, p: string) => {
|
||||
const firstWord = words.find(w => w.p === p);
|
||||
return [
|
||||
...firstWord ? [firstWord] : [],
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
import {
|
||||
Types as T,
|
||||
removeFVarients,
|
||||
} from "@lingdocs/pashto-inflector";
|
||||
|
||||
export function firstVariation(s: string): string {
|
||||
return s.split(/[,|;]/)[0].trim();
|
||||
}
|
||||
|
||||
export function psStringFromEntry(entry: T.PsString): T.PsString {
|
||||
return {
|
||||
p: entry.p,
|
||||
f: removeFVarients(entry.f),
|
||||
};
|
||||
}
|
|
@ -5,39 +5,48 @@ import {
|
|||
Types as T,
|
||||
} from "@lingdocs/pashto-inflector";
|
||||
|
||||
export function isNoun(e: Word): e is Noun {
|
||||
export function isNounEntry(e: Entry): e is NounEntry {
|
||||
if ("entry" in e) return false;
|
||||
return !!(e.c && (e.c.includes("n. m.") || e.c.includes("n. f.")));
|
||||
}
|
||||
|
||||
export function isAdjective(e: Word): e is Adjective {
|
||||
export function isAdjectiveEntry(e: Entry): e is AdjectiveEntry {
|
||||
if ("entry" in e) return false;
|
||||
return !!e.c?.includes("adj.") && !isNoun(e);
|
||||
return !!e.c?.includes("adj.") && !isNounEntry(e);
|
||||
}
|
||||
|
||||
export function isNounOrAdj(e: Word): e is (Noun | Adjective) {
|
||||
return isNoun(e) || isAdjective(e);
|
||||
export function isAdverbEntry(e: Entry): e is AdverbEntry {
|
||||
if ("entry" in e) return false;
|
||||
return !!e.c?.includes("adv.");
|
||||
}
|
||||
|
||||
export function isVerb(e: Word): e is Verb {
|
||||
export function isLocativeAdverbEntry(e: Entry): e is LocativeAdverbEntry {
|
||||
return isAdverbEntry(e) && e.c.includes("loc. adv.");
|
||||
}
|
||||
|
||||
export function isNounOrAdjEntry(e: Entry): e is (NounEntry | AdjectiveEntry) {
|
||||
return isNounEntry(e) || isAdjectiveEntry(e);
|
||||
}
|
||||
|
||||
export function isVerbEntry(e: Entry): e is VerbEntry {
|
||||
return "entry" in e && !!e.entry.c?.startsWith("v.");
|
||||
}
|
||||
|
||||
export function isMascNoun(e: Noun | Adjective): e is MascNoun {
|
||||
export function isMascNounEntry(e: NounEntry | AdjectiveEntry): e is MascNounEntry {
|
||||
return !!e.c && e.c.includes("n. m.");
|
||||
}
|
||||
|
||||
export function isFemNoun(e: Noun | Adjective): e is FemNoun {
|
||||
export function isFemNounEntry(e: NounEntry | AdjectiveEntry): e is FemNounEntry {
|
||||
return !!e.c && e.c.includes("n. f.");
|
||||
}
|
||||
|
||||
export function isUnisexNoun(e: Noun | Adjective): e is UnisexNoun {
|
||||
return isNoun(e) && e.c.includes("unisex");
|
||||
export function isUnisexNounEntry(e: NounEntry | AdjectiveEntry): e is UnisexNounEntry {
|
||||
return isNounEntry(e) && e.c.includes("unisex");
|
||||
}
|
||||
|
||||
export function isAdjOrUnisexNoun(e: Word): e is (Adjective | UnisexNoun) {
|
||||
return isAdjective(e) || (
|
||||
isNoun(e) && isUnisexNoun(e)
|
||||
export function isAdjOrUnisexNounEntry(e: Entry): e is (AdjectiveEntry | UnisexNounEntry) {
|
||||
return isAdjectiveEntry(e) || (
|
||||
isNounEntry(e) && isUnisexNounEntry(e)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -47,10 +56,10 @@ export function isAdjOrUnisexNoun(e: Word): e is (Adjective | UnisexNoun) {
|
|||
* @param e
|
||||
* @returns
|
||||
*/
|
||||
export function isPattern1Word<T extends (Noun | Adjective)>(e: T): e is Pattern1Word<T> {
|
||||
export function isPattern1Entry<T extends (NounEntry | AdjectiveEntry)>(e: T): e is Pattern1Entry<T> {
|
||||
if (e.noInf) return false;
|
||||
if (e.infap) return false;
|
||||
if (isFemNoun(e)) {
|
||||
if (isFemNounEntry(e)) {
|
||||
return (
|
||||
endsWith([{ p: "ه", f: "a" }, { p: "ح", f: "a" }], e) ||
|
||||
(endsWith({ p: pashtoConsonants }, e) && !e.c.includes("anim."))
|
||||
|
@ -69,10 +78,10 @@ export function isPattern1Word<T extends (Noun | Adjective)>(e: T): e is Pattern
|
|||
* @param e
|
||||
* @returns
|
||||
*/
|
||||
export function isPattern2Word<T extends (Noun | Adjective)>(e: T): e is Pattern2Word<T> {
|
||||
export function isPattern2Entry<T extends (NounEntry | AdjectiveEntry)>(e: T): e is Pattern2Entry<T> {
|
||||
if (e.noInf) return false;
|
||||
if (e.infap) return false;
|
||||
if (isFemNoun(e)) {
|
||||
if (isFemNounEntry(e)) {
|
||||
return !e.c.includes("pl.") && endsWith({ p: "ې", f: "e" }, e, true);
|
||||
}
|
||||
// TODO: check if it's a single syllable word, in which case it would be pattern 1
|
||||
|
@ -85,10 +94,10 @@ export function isPattern2Word<T extends (Noun | Adjective)>(e: T): e is Pattern
|
|||
* @param e
|
||||
* @returns
|
||||
*/
|
||||
export function isPattern3Word<T extends (Noun | Adjective)>(e: T): e is Pattern3Word<T> {
|
||||
export function isPattern3Entry<T extends (NounEntry | AdjectiveEntry)>(e: T): e is Pattern3Entry<T> {
|
||||
if (e.noInf) return false;
|
||||
if (e.infap) return false;
|
||||
if (isFemNoun(e)) {
|
||||
if (isFemNounEntry(e)) {
|
||||
return endsWith({ p: "ۍ" }, e);
|
||||
}
|
||||
return (countSyllables(e.f) > 1)
|
||||
|
@ -102,7 +111,7 @@ export function isPattern3Word<T extends (Noun | Adjective)>(e: T): e is Pattern
|
|||
* @param e
|
||||
* @returns
|
||||
*/
|
||||
export function isPattern4Word<T extends (Noun | Adjective)>(e: T): e is Pattern4Word<T> {
|
||||
export function isPattern4Entry<T extends (NounEntry | AdjectiveEntry)>(e: T): e is Pattern4Entry<T> {
|
||||
if (e.noInf) return false;
|
||||
return (
|
||||
!!(e.infap && e.infaf && e.infbp && e.infbf)
|
||||
|
@ -117,7 +126,7 @@ export function isPattern4Word<T extends (Noun | Adjective)>(e: T): e is Pattern
|
|||
* @param e
|
||||
* @returns
|
||||
*/
|
||||
export function isPattern5Word<T extends (Noun | Adjective)>(e: T): e is Pattern5Word<T> {
|
||||
export function isPattern5Entry<T extends (NounEntry | AdjectiveEntry)>(e: T): e is Pattern5Entry<T> {
|
||||
if (e.noInf) return false;
|
||||
return (
|
||||
!!(e.infap && e.infaf && e.infbp && e.infbf)
|
||||
|
@ -128,18 +137,18 @@ export function isPattern5Word<T extends (Noun | Adjective)>(e: T): e is Pattern
|
|||
);
|
||||
}
|
||||
|
||||
export function isPattern6FemNoun(e: FemNoun): e is Pattern6FemNoun<FemNoun> {
|
||||
if (!isFemNoun(e)) return false;
|
||||
export function isPattern6FemEntry(e: FemNounEntry): e is Pattern6FemEntry<FemNounEntry> {
|
||||
if (!isFemNounEntry(e)) return false;
|
||||
if (e.c.includes("anim.")) return false;
|
||||
return e.p.slice(-1) === "ي";
|
||||
}
|
||||
|
||||
export function isPluralEntry<U extends Noun>(e: U): e is PluralEntry<U> {
|
||||
export function isPluralNounEntry<U extends NounEntry>(e: U): e is PluralNounEntry<U> {
|
||||
return e.c.includes("pl.");
|
||||
}
|
||||
|
||||
export function isSingularEntry<U extends Noun>(e: U): e is SingularEntry<U> {
|
||||
return !isPluralEntry(e);
|
||||
export function isSingularEntry<U extends NounEntry>(e: U): e is SingularEntry<U> {
|
||||
return !isPluralNounEntry(e);
|
||||
}
|
||||
|
||||
export function isArrayOneOrMore<U>(a: U[]): a is T.ArrayOneOrMore<U> {
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
type Progress = {
|
||||
total: number,
|
||||
current: number,
|
||||
};
|
||||
|
||||
type Current<T> = {
|
||||
progress: Progress,
|
||||
question: T,
|
||||
};
|
||||
|
||||
type QuestionGenerator<T> = Generator<Current<T>, void, unknown>;
|
||||
|
||||
type QuestionDisplayProps<T> = {
|
||||
question: T,
|
||||
callback: (correct: boolean) => void,
|
||||
};
|
||||
|
||||
type GameRecord = {
|
||||
title: string,
|
||||
id: string,
|
||||
studyLink: string,
|
||||
Game: () => JSX.Element,
|
||||
};
|
||||
|
||||
type Noun = import("@lingdocs/pashto-inflector").Types.DictionaryEntry & { c: string } & { __brand: "a noun entry" };
|
||||
type MascNoun = Noun & { __brand2: "a masc noun entry" };
|
||||
type FemNoun = Noun & { __brand2: "a fem noun entry" };
|
||||
type UnisexNoun = MascNoun & { __brand3: "a unisex noun entry" };
|
||||
type Adjective = import("@lingdocs/pashto-inflector").Types.DictionaryEntry & { c: string } & { __brand: "an adjective entry" };
|
||||
type Verb = {
|
||||
entry: import("@lingdocs/pashto-inflector").Types.DictionaryEntry & { __brand: "a verb entry" },
|
||||
complement?: import("@lingdocs/pashto-inflector").Types.DictionaryEntry,
|
||||
};
|
||||
|
||||
type SingularEntry<T extends Noun> = T & { __brand7: "a singular noun - as opposed to an always plural noun" };
|
||||
type PluralEntry<T extends Noun> = T & { __brand7: "a noun that is always plural" };
|
||||
|
||||
type RawWord = T.DictionaryEntry | {
|
||||
entry: T.DictionaryEntry,
|
||||
complement?: T.DictionaryEntry,
|
||||
};
|
||||
|
||||
// TODO: Write type predicates for these
|
||||
type Pattern1Word<T> = T & { __brand3: "basic inflection pattern" };
|
||||
type Pattern2Word<T> = T & { __brand3: "ending in unstressed ی pattern" };
|
||||
type Pattern3Word<T> = T & { __brand3: "ending in stressed ی pattern" };
|
||||
type Pattern4Word<T> = T & { __brand3: "Pashtoon pattern" };
|
||||
type Pattern5Word<T> = T & { __brand3: "short squish pattern" };
|
||||
type Pattern6FemNoun<T extends FemNoun> = FemNoun & { __brand3: "non anim. ending in ي" };
|
||||
type NonInflecting<T> = T & { __brand3: "non-inflecting" };
|
||||
// PLUS FEM INFLECTING
|
||||
|
||||
type Word = Noun | Adjective | Verb;
|
||||
|
||||
type Words = {
|
||||
nouns: Noun[],
|
||||
adjectives: Adjective[],
|
||||
verbs: Verb[],
|
||||
}
|
||||
|
||||
type EquativeTense = "present" | "subjunctive" | "past" | "future" | "wouldBe" | "pastSubjunctive";
|
|
@ -0,0 +1,32 @@
|
|||
type NounEntry = import("@lingdocs/pashto-inflector").Types.DictionaryEntry & { c: string } & { __brand: "a noun entry" };
|
||||
type MascNounEntry = NounEntry & { __brand2: "a masc noun entry" };
|
||||
type FemNounEntry = NounEntry & { __brand2: "a fem noun entry" };
|
||||
type UnisexNounEntry = MascNounEntry & { __brand3: "a unisex noun entry" };
|
||||
type AdverbEntry = import("@lingdocs/pashto-inflector").Types.DictionaryEntry & { c: string } & { __brand: "an adverb entry" };
|
||||
type LocativeAdverbEntry = AdverbEntry & { __brand2: "a locative adverb entry" };
|
||||
type AdjectiveEntry = import("@lingdocs/pashto-inflector").Types.DictionaryEntry & { c: string } & { __brand: "an adjective entry" };
|
||||
type VerbEntry = {
|
||||
entry: import("@lingdocs/pashto-inflector").Types.DictionaryEntry & { __brand: "a verb entry" },
|
||||
// TODO: the compliment could also be typed? Maybe?
|
||||
complement?: import("@lingdocs/pashto-inflector").Types.DictionaryEntry,
|
||||
};
|
||||
|
||||
type SingularEntry<T extends NounEntry> = T & { __brand7: "a singular noun - as opposed to an always plural noun" };
|
||||
type PluralNounEntry<T extends NounEntry> = T & { __brand7: "a noun that is always plural" };
|
||||
|
||||
type Pattern1Entry<T> = T & { __brand3: "basic inflection pattern" };
|
||||
type Pattern2Entry<T> = T & { __brand3: "ending in unstressed ی pattern" };
|
||||
type Pattern3Entry<T> = T & { __brand3: "ending in stressed ی pattern" };
|
||||
type Pattern4Entry<T> = T & { __brand3: "Pashtoon pattern" };
|
||||
type Pattern5Entry<T> = T & { __brand3: "short squish pattern" };
|
||||
type Pattern6FemEntry<T extends FemNounEntry> = T & { __brand3: "non anim. ending in ي" };
|
||||
type NonInflecting<T> = T & { __brand3: "non-inflecting" };
|
||||
|
||||
type Entry = NounEntry | AdjectiveEntry | AdverbEntry | VerbEntry;
|
||||
|
||||
type Words = {
|
||||
nouns: NounEntry[],
|
||||
adjectives: AdjectiveEntry[],
|
||||
verbs: VerbEntry[],
|
||||
adverbs: AdverbEntry[],
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
type Progress = {
|
||||
total: number,
|
||||
current: number,
|
||||
};
|
||||
|
||||
type Current<T> = {
|
||||
progress: Progress,
|
||||
question: T,
|
||||
};
|
||||
|
||||
type QuestionGenerator<T> = Generator<Current<T>, void, unknown>;
|
||||
|
||||
type QuestionDisplayProps<T> = {
|
||||
question: T,
|
||||
callback: (correct: boolean) => void,
|
||||
};
|
||||
|
||||
type GameRecord = {
|
||||
title: string,
|
||||
id: string,
|
||||
studyLink: string,
|
||||
Game: () => JSX.Element,
|
||||
};
|
|
@ -0,0 +1,63 @@
|
|||
type EquativeTense = "present" | "subjunctive" | "past" | "future" | "wouldBe" | "pastSubjunctive";
|
||||
type NounNumber = "singular" | "plural";
|
||||
|
||||
type EquativeClause = {
|
||||
subject: NounPhrase,
|
||||
predicate: NounPhrase | Compliment,
|
||||
tense: EquativeTense,
|
||||
};
|
||||
|
||||
type EquativeClauseOutput = {
|
||||
subject: {
|
||||
ps: (import("@lingdocs/pashto-inflector").Types.PsString)[],
|
||||
e: string,
|
||||
},
|
||||
predicate: {
|
||||
ps: (import("@lingdocs/pashto-inflector").Types.PsString)[],
|
||||
e: string,
|
||||
},
|
||||
ba: boolean,
|
||||
equative: {
|
||||
ps: import("@lingdocs/pashto-inflector").Types.SentenceForm,
|
||||
e: string[],
|
||||
},
|
||||
};
|
||||
|
||||
type NounPhrase = Pronoun | Noun | Participle;
|
||||
|
||||
// TODO: better, simpler type here
|
||||
type Noun = {
|
||||
type: "unisex noun",
|
||||
number: NounNumber,
|
||||
gender: import("@lingdocs/pashto-inflector").Types.Gender,
|
||||
entry: UnisexNounEntry,
|
||||
possesor?: Noun,
|
||||
adjectives?: AdjectiveEntry[],
|
||||
} | {
|
||||
type: "plural noun",
|
||||
entry: PluralNounEntry<MascNounEntry | FemNounEntry>,
|
||||
possesor?: Noun,
|
||||
adjectives?: AdjectiveEntry[],
|
||||
} | {
|
||||
type: "singular noun",
|
||||
number: NounNumber,
|
||||
entry: SingularEntry<MascNounEntry | FemNounEntry>,
|
||||
possesor?: Noun,
|
||||
adjectives?: AdjectiveEntry[],
|
||||
};
|
||||
|
||||
type Compliment = {
|
||||
type: "compliment",
|
||||
entry: AdjectiveEntry | LocativeAdverbEntry | NounEntry,
|
||||
};
|
||||
|
||||
type Participle = {
|
||||
type: "participle",
|
||||
entry: VerbEntry,
|
||||
}
|
||||
|
||||
type Pronoun = {
|
||||
type: "pronoun",
|
||||
pronounType: "near" | "far",
|
||||
person: import("@lingdocs/pashto-inflector").Types.Person,
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
module.exports = [
|
||||
{ ts: 1527811221, e: `above, overhead` }, // پورته - porta
|
||||
{ ts: 1578080952673, e: `outside, outside of, beyond` }, // دباندې - dubaande
|
||||
{ ts: 1527812558, e: `here` }, // دلته - dălta
|
||||
{ ts: 1527812449, e: `there` }, // هلته - halta
|
||||
{ ts: 1527813122, e: `inside, within, interior` }, // دننه - dununa
|
||||
{ ts: 1527812780, e: `down, beneath` }, // ښکته - xkuta
|
||||
{ ts: 1527814605, e: `far, distant` }, // لرې - lure
|
||||
{ ts: 1527814708, e: `close, near, soon, almost` }, // نژدې - nijzde, najzde
|
||||
{ ts: 1527814911, e: `near, close to, almost` }, // نږدې - naGde
|
||||
];
|
|
@ -0,0 +1,5 @@
|
|||
module.exports = [
|
||||
{"ts":1527812798,"i":5593,"p":"خفه","f":"khúfa","g":"khufa","e":"sad, upset, angry; choked, suffocated","c":"adj."},
|
||||
{"ts":1527812792,"i":5773,"p":"خوشاله","f":"khoshaala","g":"khoshaala","e":"happy, glad","c":"adj."},
|
||||
{"ts":1527812761,"i":8534,"p":"ښایسته","f":"xáaysta","g":"xaayusta","e":"beautiful","c":"adj."},
|
||||
];
|
File diff suppressed because one or more lines are too long
|
@ -1,20 +1,22 @@
|
|||
import rawWords from "./raw-words";
|
||||
import {
|
||||
isAdjective,
|
||||
isNoun,
|
||||
isVerb,
|
||||
isAdjectiveEntry,
|
||||
isNounEntry,
|
||||
isVerbEntry,
|
||||
isAdverbEntry,
|
||||
} from "../lib/type-predicates";
|
||||
import { categorize } from "../lib/categorize";
|
||||
|
||||
const words = categorize<Word, Words>(rawWords, {
|
||||
"nouns": isNoun,
|
||||
"adjectives": isAdjective,
|
||||
"verbs": isVerb,
|
||||
const words = categorize<Entry, Words>(rawWords, {
|
||||
"nouns": isNounEntry,
|
||||
"adjectives": isAdjectiveEntry,
|
||||
"verbs": isVerbEntry,
|
||||
"adverbs": isAdverbEntry,
|
||||
});
|
||||
|
||||
export default words;
|
||||
|
||||
export const { nouns, adjectives, verbs } = words;
|
||||
export const { nouns, adjectives, verbs, adverbs } = words;
|
||||
|
||||
// console.log(
|
||||
// Object.entries(
|
||||
|
|
|
@ -1684,10 +1684,10 @@
|
|||
pbf "^3.2.1"
|
||||
rambda "^6.7.0"
|
||||
|
||||
"@lingdocs/pashto-inflector@^1.3.2":
|
||||
version "1.3.2"
|
||||
resolved "https://npm.lingdocs.com/@lingdocs%2fpashto-inflector/-/pashto-inflector-1.3.2.tgz#a5501d9a56c8590eee052a88ffdbe38a07594f29"
|
||||
integrity sha512-crZlvjLssb+Q6kh6sAHlg4A4BerJnurSFUMz0fyYl3PlULsZyVHGvKepHYhP5AXyWt34zGiTzHTWapzIWsWtiQ==
|
||||
"@lingdocs/pashto-inflector@^1.3.5":
|
||||
version "1.3.5"
|
||||
resolved "https://npm.lingdocs.com/@lingdocs%2fpashto-inflector/-/pashto-inflector-1.3.5.tgz#cfc311462c686e5591f12ee68b333a2ec479f428"
|
||||
integrity sha512-Asxu773Z9+0Gxi8wkSD6x3JYcSeJmXqgoHQu1Hvph1oOAac3t8Bn0uXC/OGO5p1leilEHePJT+hkMFmURTArcw==
|
||||
dependencies:
|
||||
classnames "^2.2.6"
|
||||
pbf "^3.2.1"
|
||||
|
|
Loading…
Reference in New Issue