with beta search conjugation
This commit is contained in:
parent
074f028439
commit
17738972cc
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@lingdocs/pashto-inflector",
|
"name": "@lingdocs/pashto-inflector",
|
||||||
"version": "0.7.1",
|
"version": "0.7.2",
|
||||||
"author": "lingdocs.com",
|
"author": "lingdocs.com",
|
||||||
"description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations",
|
"description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations",
|
||||||
"homepage": "https://verbs.lingdocs.com",
|
"homepage": "https://verbs.lingdocs.com",
|
||||||
|
|
|
@ -8,7 +8,24 @@
|
||||||
|
|
||||||
import {
|
import {
|
||||||
parseEc,
|
parseEc,
|
||||||
|
personFromVerbBlockPos,
|
||||||
} from "./misc-helpers";
|
} from "./misc-helpers";
|
||||||
|
import * as T from "../types";
|
||||||
|
|
||||||
|
test("personFromVerbBlockPos should work", () => {
|
||||||
|
expect(personFromVerbBlockPos([0, 0])).toEqual(T.Person.FirstSingMale);
|
||||||
|
expect(personFromVerbBlockPos([1, 0])).toEqual(T.Person.FirstSingFemale);
|
||||||
|
expect(personFromVerbBlockPos([2, 0])).toEqual(T.Person.SecondSingMale);
|
||||||
|
expect(personFromVerbBlockPos([3, 0])).toEqual(T.Person.SecondSingFemale);
|
||||||
|
expect(personFromVerbBlockPos([4, 0])).toEqual(T.Person.ThirdSingMale);
|
||||||
|
expect(personFromVerbBlockPos([5, 0])).toEqual(T.Person.ThirdSingFemale);
|
||||||
|
expect(personFromVerbBlockPos([0, 1])).toEqual(T.Person.FirstPlurMale);
|
||||||
|
expect(personFromVerbBlockPos([1, 1])).toEqual(T.Person.FirstPlurFemale);
|
||||||
|
expect(personFromVerbBlockPos([2, 1])).toEqual(T.Person.SecondPlurMale);
|
||||||
|
expect(personFromVerbBlockPos([3, 1])).toEqual(T.Person.SecondPlurFemale);
|
||||||
|
expect(personFromVerbBlockPos([4, 1])).toEqual(T.Person.ThirdPlurMale);
|
||||||
|
expect(personFromVerbBlockPos([5, 1])).toEqual(T.Person.ThirdPlurFemale);
|
||||||
|
})
|
||||||
|
|
||||||
test("parseEc should work", () => {
|
test("parseEc should work", () => {
|
||||||
expect(parseEc("walk")).toEqual(["walk", "walks", "walking", "walked", "walked"]);
|
expect(parseEc("walk")).toEqual(["walk", "walks", "walking", "walked", "walked"]);
|
||||||
|
|
|
@ -86,6 +86,10 @@ export function getPersonNumber(gender: "masc" | "fem", number: "singular" | "pl
|
||||||
return base + (number === "singular" ? 0 : 6);
|
return base + (number === "singular" ? 0 : 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function personFromVerbBlockPos(pos: [number, number]): T.Person {
|
||||||
|
return pos[0] + (pos[1] === 1 ? 6 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
export function getPersonInflectionsKey(person: T.Person): T.PersonInflectionsField {
|
export function getPersonInflectionsKey(person: T.Person): T.PersonInflectionsField {
|
||||||
return `${personGender(person)}${personIsPlural(person) ? "Plur" : "Sing"}` as T.PersonInflectionsField;
|
return `${personGender(person)}${personIsPlural(person) ? "Plur" : "Sing"}` as T.PersonInflectionsField;
|
||||||
}
|
}
|
||||||
|
@ -265,3 +269,17 @@ export function parseEc(ec: string): T.EnglishVerbConjugationEc {
|
||||||
// }
|
// }
|
||||||
// return Array.isArray(f) && f.length === 2 && isPersonLine(f[0]);
|
// return Array.isArray(f) && f.length === 2 && isPersonLine(f[0]);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
const b = {
|
||||||
|
a: "foo",
|
||||||
|
b: "bar",
|
||||||
|
c: {
|
||||||
|
d: "foo",
|
||||||
|
e: "bar",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// export function findPathsToItem(block: any, s: string): string[][] {
|
||||||
|
// const keys = Object.keys(block);
|
||||||
|
// return keys.reduce()
|
||||||
|
// }
|
|
@ -242,7 +242,7 @@ function getMatchingInflection(
|
||||||
return infs[persNum % 2 === 0 ? "masc" : "fem"][singPlur][0];
|
return infs[persNum % 2 === 0 ? "masc" : "fem"][singPlur][0];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isVerbBlock(x: any) {
|
export function isVerbBlock(x: unknown) {
|
||||||
return (
|
return (
|
||||||
Array.isArray(x) &&
|
Array.isArray(x) &&
|
||||||
(x.length === 6) &&
|
(x.length === 6) &&
|
||||||
|
@ -250,6 +250,22 @@ export function isVerbBlock(x: any) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isImperativeBlock(x: unknown) {
|
||||||
|
return (
|
||||||
|
Array.isArray(x) &&
|
||||||
|
(x.length === 2) &&
|
||||||
|
"p" in x[0][0][0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isInflectionSet(x: any): boolean {
|
||||||
|
return (
|
||||||
|
Array.isArray(x)
|
||||||
|
&& (x.length === 3)
|
||||||
|
&& "p" in x[0][0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
type toAddToForm = Array<" " | T.SingleOrLengthOpts<T.PsString> | T.SingleOrLengthOpts<T.UnisexInflections> | T.SingleOrLengthOpts<T.PsString>[] | T.OptionalPersonInflections<T.PsString> | T.VerbBlock>;
|
type toAddToForm = Array<" " | T.SingleOrLengthOpts<T.PsString> | T.SingleOrLengthOpts<T.UnisexInflections> | T.SingleOrLengthOpts<T.PsString>[] | T.OptionalPersonInflections<T.PsString> | T.VerbBlock>;
|
||||||
type toAddToFormLengthChosen = Array<" " | T.PsString | T.UnisexInflections | T.PsString[] | T.OptionalPersonInflections<T.PsString> | T.VerbBlock>;
|
type toAddToFormLengthChosen = Array<" " | T.PsString | T.UnisexInflections | T.PsString[] | T.OptionalPersonInflections<T.PsString> | T.VerbBlock>;
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { searchConjugation } from "./search-conjugation";
|
||||||
|
import { conjugateVerb } from "./verb-conjugation";
|
||||||
|
import { inflectWord } from "./pashto-inflector";
|
||||||
|
import * as T from "../types";
|
||||||
|
|
||||||
|
test("search should find form", () => {
|
||||||
|
const conjugation = conjugateVerb(
|
||||||
|
{ i: 0, ts: 1, p: "لیکل", f: "leekul", g: "leekul", e: "to write", c: "v. trans."},
|
||||||
|
"aay",
|
||||||
|
);
|
||||||
|
const result = searchConjugation(conjugation as any, "لیکلای شي");
|
||||||
|
console.log(JSON.stringify(result));
|
||||||
|
expect(result).toBeTruthy();
|
||||||
|
const inflection = inflectWord({ i: 0, ts: 1, p: "غټ", f: "ghuT", g: "ghuT", e: "big", c: "adj." }) as T.Inflections;
|
||||||
|
const iResult = searchConjugation(inflection, "غټو");
|
||||||
|
console.log(JSON.stringify(iResult));
|
||||||
|
// expect(searchConjugation(conjugation, "ولیکلې"))
|
||||||
|
// .toEqual([{
|
||||||
|
// form: ["perfective", "past", "long"],
|
||||||
|
// person: T.Person.ThirdPlurFemale,
|
||||||
|
// }]);
|
||||||
|
// expect(searchConjugation(conjugation, "ولیکلو"))
|
||||||
|
// .toEqual([{
|
||||||
|
// form: ["perfective", "past", "long"],
|
||||||
|
// person: T.Person.ThirdSingMale,
|
||||||
|
// }]);
|
||||||
|
})
|
|
@ -0,0 +1,129 @@
|
||||||
|
import * as T from "../types";
|
||||||
|
import {
|
||||||
|
isVerbBlock,
|
||||||
|
isImperativeBlock,
|
||||||
|
isInflectionSet,
|
||||||
|
} from "./p-text-helpers";
|
||||||
|
import { personFromVerbBlockPos } from "./misc-helpers";
|
||||||
|
|
||||||
|
const inflectionNames: InflectionName[] = ["plain", "1st", "2nd"];
|
||||||
|
|
||||||
|
type ObPile = { [key: string]: ObRec; }
|
||||||
|
type ObRec = any[] | ObPile;
|
||||||
|
|
||||||
|
type ConjSearchResults = {
|
||||||
|
form: string[],
|
||||||
|
position: InflectionName[] | T.Person[],
|
||||||
|
}[];
|
||||||
|
|
||||||
|
type BlockResult = {
|
||||||
|
blockResult: true,
|
||||||
|
position: InflectionName[] | T.Person[],
|
||||||
|
}
|
||||||
|
|
||||||
|
type InflectionName = "plain" | "1st" | "2nd";
|
||||||
|
|
||||||
|
export function searchConjugation(pile: ObPile, s: string): ConjSearchResults {
|
||||||
|
|
||||||
|
function searchObRecord(record: ObRec): null | BlockResult | ConjSearchResults {
|
||||||
|
// hit a bottom part a tree, see if what we're looking for is there
|
||||||
|
if (Array.isArray(record)) {
|
||||||
|
return searchBlock(record, s);
|
||||||
|
}
|
||||||
|
// look further down the tree recursively
|
||||||
|
return searchConjugation(record, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.entries(pile).reduce((res: ConjSearchResults, entry) => {
|
||||||
|
const [name, value] = entry;
|
||||||
|
if (name === "info") {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
// search for value from key
|
||||||
|
const result = searchObRecord(value);
|
||||||
|
// Result: Hit the bottom and nothing found
|
||||||
|
if (result === null) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
// Result: Hit the bottom and found what we were looking for
|
||||||
|
// add in the path and position
|
||||||
|
if ("blockResult" in result) {
|
||||||
|
return [
|
||||||
|
...res,
|
||||||
|
{
|
||||||
|
form: [name],
|
||||||
|
position: result.position,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
// Result: Have to keep looking down recursively
|
||||||
|
// add in the current path to all the results
|
||||||
|
const rb: ConjSearchResults = [
|
||||||
|
...res,
|
||||||
|
...result.map((r) => ({
|
||||||
|
...r,
|
||||||
|
form: [name, ...r.form],
|
||||||
|
})),
|
||||||
|
]
|
||||||
|
return rb;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchBlock(block: any[], s: string): null | BlockResult {
|
||||||
|
if (isVerbBlock(block)) {
|
||||||
|
const verbBlock = block as T.VerbBlock;
|
||||||
|
const position = searchVerbBlock(verbBlock, s);
|
||||||
|
if (position.length) {
|
||||||
|
return {
|
||||||
|
blockResult: true,
|
||||||
|
position: position.map(pos => personFromVerbBlockPos(pos)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isImperativeBlock(block)) {
|
||||||
|
const ImperativeBlock = block as T.ImperativeBlock;
|
||||||
|
const position = searchVerbBlock(ImperativeBlock, s);
|
||||||
|
if (position.length) {
|
||||||
|
return {
|
||||||
|
blockResult: true,
|
||||||
|
position: position.map(pos => personFromVerbBlockPos([pos[0] + 2, pos[1]])),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isInflectionSet(block)) {
|
||||||
|
const inflectionSet = block as T.InflectionSet;
|
||||||
|
const position = searchInflectionSet(inflectionSet, s);
|
||||||
|
if (position.length) {
|
||||||
|
return {
|
||||||
|
blockResult: true,
|
||||||
|
position: position,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchVerbBlock(vb: T.VerbBlock | T.ImperativeBlock, s: string): [number, number][] {
|
||||||
|
function searchRow(row: T.PersonLine): (0 | 1)[] {
|
||||||
|
return row.reduce((all: (0 | 1)[], item, i: number): (0 | 1)[] => {
|
||||||
|
const c = item.some(ps => ps.p === s)
|
||||||
|
? [...all, i as 0 | 1]
|
||||||
|
: all
|
||||||
|
return c;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
return vb.reduce((found: [number, number][], row, i): [number, number][] => {
|
||||||
|
const inRow = searchRow(row);
|
||||||
|
if (inRow.length === 0) return found;
|
||||||
|
return [...found, ...inRow.map((f): [number, number] => [i, f])];
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchInflectionSet(inf: T.InflectionSet, s: string): InflectionName[] {
|
||||||
|
return inf.reduce((found: InflectionName[], item, i): InflectionName[] => {
|
||||||
|
if (item.some((ps) => ps.p === s)) {
|
||||||
|
return [...found, inflectionNames[i]];
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}, []);
|
||||||
|
}
|
|
@ -142,7 +142,7 @@ export const replacerInfo: IReplacerInfoItem[] = [
|
||||||
{
|
{
|
||||||
char: "eyy",
|
char: "eyy",
|
||||||
alalc: "ạy",
|
alalc: "ạy",
|
||||||
ipa: "əj",
|
ipa: "ɛ̝j",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
char: "e",
|
char: "e",
|
||||||
|
|
|
@ -25,6 +25,7 @@ import VerbFormDisplay from "./components/VerbFormDisplay";
|
||||||
import VerbTable from "./components/VerbTable";
|
import VerbTable from "./components/VerbTable";
|
||||||
import Examples from "./components/Examples";
|
import Examples from "./components/Examples";
|
||||||
import VerbInfo, { RootsAndStems } from "./components/verb-info/VerbInfo";
|
import VerbInfo, { RootsAndStems } from "./components/verb-info/VerbInfo";
|
||||||
|
import { searchConjugation } from "./lib/search-conjugation";
|
||||||
import {
|
import {
|
||||||
addToForm,
|
addToForm,
|
||||||
concatPsString,
|
concatPsString,
|
||||||
|
@ -88,6 +89,7 @@ export {
|
||||||
phoneticsToDiacritics,
|
phoneticsToDiacritics,
|
||||||
addDiacritics,
|
addDiacritics,
|
||||||
translatePhonetics,
|
translatePhonetics,
|
||||||
|
searchConjugation,
|
||||||
// protobuf helpers
|
// protobuf helpers
|
||||||
readDictionary,
|
readDictionary,
|
||||||
writeDictionary,
|
writeDictionary,
|
||||||
|
|
14
src/types.ts
14
src/types.ts
|
@ -329,16 +329,16 @@ export type PerfectContent = {
|
||||||
pastSubjunctiveHypothetical: VerbForm; // PPART + waay
|
pastSubjunctiveHypothetical: VerbForm; // PPART + waay
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Plain, 1st, and 2nd Inflection
|
||||||
|
export type InflectionSet = ArrayFixed<ArrayOneOrMore<PsString>, 3>;
|
||||||
|
|
||||||
export type UnisexInflections = {
|
export type UnisexInflections = {
|
||||||
masc: ArrayFixed<ArrayOneOrMore<PsString>, 3>,
|
masc: InflectionSet,
|
||||||
fem: ArrayFixed<ArrayOneOrMore<PsString>, 3>,
|
fem: InflectionSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Inflections = UnisexInflections | {
|
export type Inflections = UnisexInflections
|
||||||
masc: ArrayFixed<ArrayOneOrMore<PsString>, 3>,
|
| Omit<UnisexInflections, "fem"> | Omit<UnisexInflections, "masc">;
|
||||||
} | {
|
|
||||||
fem: ArrayFixed<ArrayOneOrMore<PsString>, 3>,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PersonLine = [
|
export type PersonLine = [
|
||||||
/** singular form of person */
|
/** singular form of person */
|
||||||
|
|
Loading…
Reference in New Issue