diff --git a/package.json b/package.json index ef36940..9fec467 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lingdocs/pashto-inflector", - "version": "0.7.1", + "version": "0.7.2", "author": "lingdocs.com", "description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations", "homepage": "https://verbs.lingdocs.com", diff --git a/src/lib/misc-helpers.test.ts b/src/lib/misc-helpers.test.ts index 3592299..0223917 100644 --- a/src/lib/misc-helpers.test.ts +++ b/src/lib/misc-helpers.test.ts @@ -8,7 +8,24 @@ import { parseEc, + personFromVerbBlockPos, } 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", () => { expect(parseEc("walk")).toEqual(["walk", "walks", "walking", "walked", "walked"]); diff --git a/src/lib/misc-helpers.ts b/src/lib/misc-helpers.ts index ea8f3c7..eb32837 100644 --- a/src/lib/misc-helpers.ts +++ b/src/lib/misc-helpers.ts @@ -86,6 +86,10 @@ export function getPersonNumber(gender: "masc" | "fem", number: "singular" | "pl 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 { return `${personGender(person)}${personIsPlural(person) ? "Plur" : "Sing"}` as T.PersonInflectionsField; } @@ -264,4 +268,18 @@ export function parseEc(ec: string): T.EnglishVerbConjugationEc { // return Array.isArray(g) && Array.isArray(g[0]) && "p" in g[0][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() // } \ No newline at end of file diff --git a/src/lib/p-text-helpers.ts b/src/lib/p-text-helpers.ts index 7987223..d0b325b 100644 --- a/src/lib/p-text-helpers.ts +++ b/src/lib/p-text-helpers.ts @@ -242,7 +242,7 @@ function getMatchingInflection( return infs[persNum % 2 === 0 ? "masc" : "fem"][singPlur][0]; } -export function isVerbBlock(x: any) { +export function isVerbBlock(x: unknown) { return ( Array.isArray(x) && (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.SingleOrLengthOpts | T.SingleOrLengthOpts[] | T.OptionalPersonInflections | T.VerbBlock>; type toAddToFormLengthChosen = Array<" " | T.PsString | T.UnisexInflections | T.PsString[] | T.OptionalPersonInflections | T.VerbBlock>; diff --git a/src/lib/search-conjugation.test.ts b/src/lib/search-conjugation.test.ts new file mode 100644 index 0000000..0ad1b65 --- /dev/null +++ b/src/lib/search-conjugation.test.ts @@ -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, + // }]); +}) \ No newline at end of file diff --git a/src/lib/search-conjugation.ts b/src/lib/search-conjugation.ts new file mode 100644 index 0000000..1f1d485 --- /dev/null +++ b/src/lib/search-conjugation.ts @@ -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; + }, []); +} \ No newline at end of file diff --git a/src/lib/translate-phonetics-replacer.ts b/src/lib/translate-phonetics-replacer.ts index e04616f..e00a16f 100644 --- a/src/lib/translate-phonetics-replacer.ts +++ b/src/lib/translate-phonetics-replacer.ts @@ -142,7 +142,7 @@ export const replacerInfo: IReplacerInfoItem[] = [ { char: "eyy", alalc: "ạy", - ipa: "əj", + ipa: "ɛ̝j", }, { char: "e", diff --git a/src/library.ts b/src/library.ts index 8b5a34f..f413801 100644 --- a/src/library.ts +++ b/src/library.ts @@ -25,6 +25,7 @@ import VerbFormDisplay from "./components/VerbFormDisplay"; import VerbTable from "./components/VerbTable"; import Examples from "./components/Examples"; import VerbInfo, { RootsAndStems } from "./components/verb-info/VerbInfo"; +import { searchConjugation } from "./lib/search-conjugation"; import { addToForm, concatPsString, @@ -88,6 +89,7 @@ export { phoneticsToDiacritics, addDiacritics, translatePhonetics, + searchConjugation, // protobuf helpers readDictionary, writeDictionary, diff --git a/src/types.ts b/src/types.ts index 7a84446..d030f48 100644 --- a/src/types.ts +++ b/src/types.ts @@ -329,16 +329,16 @@ export type PerfectContent = { pastSubjunctiveHypothetical: VerbForm; // PPART + waay } +// Plain, 1st, and 2nd Inflection +export type InflectionSet = ArrayFixed, 3>; + export type UnisexInflections = { - masc: ArrayFixed, 3>, - fem: ArrayFixed, 3>, + masc: InflectionSet, + fem: InflectionSet, } -export type Inflections = UnisexInflections | { - masc: ArrayFixed, 3>, -} | { - fem: ArrayFixed, 3>, -} +export type Inflections = UnisexInflections + | Omit | Omit; export type PersonLine = [ /** singular form of person */