diff --git a/package-lock.json b/package-lock.json index a617848..4185b05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pashto-inflector", - "version": "7.0.9", + "version": "7.0.11", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "pashto-inflector", - "version": "7.0.9", + "version": "7.0.11", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 837bf46..7dd8df2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pashto-inflector", - "version": "7.0.9", + "version": "7.0.11", "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/components/package-lock.json b/src/components/package-lock.json index 325fac2..7185e08 100644 --- a/src/components/package-lock.json +++ b/src/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@lingdocs/ps-react", - "version": "7.0.9", + "version": "7.0.11", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@lingdocs/ps-react", - "version": "7.0.9", + "version": "7.0.11", "license": "MIT", "dependencies": { "@formkit/auto-animate": "^1.0.0-beta.3", diff --git a/src/components/package.json b/src/components/package.json index 23a5942..d557a81 100644 --- a/src/components/package.json +++ b/src/components/package.json @@ -1,6 +1,6 @@ { "name": "@lingdocs/ps-react", - "version": "7.0.9", + "version": "7.0.11", "description": "Pashto inflector library module with React components", "main": "dist/components/library.js", "module": "dist/components/library.js", diff --git a/src/components/src/verb-info/non-faded-tree.svg b/src/components/src/verb-info/non-faded-tree.svg new file mode 100644 index 0000000..a0c9b79 --- /dev/null +++ b/src/components/src/verb-info/non-faded-tree.svg @@ -0,0 +1,715 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2013-09-30T21:43:37 + Oak Tree + https://openclipart.org/detail/184451/tree-by-flooredmusic-184451 + + + flooredmusic + + + + + comic + forest + nature + oak + tree + view + wood + woods + + + + + + + + + + + diff --git a/src/components/src/vp-explorer/VPExplorer.tsx b/src/components/src/vp-explorer/VPExplorer.tsx index 6b74d8a..d01aa68 100644 --- a/src/components/src/vp-explorer/VPExplorer.tsx +++ b/src/components/src/vp-explorer/VPExplorer.tsx @@ -10,7 +10,10 @@ import { completeVPSelection } from "../../../lib/src/phrase-building/vp-tools"; import VPExplorerQuiz from "./VPExplorerQuiz"; // @ts-ignore import LZString from "lz-string"; -import { vpsReducer } from "../../../lib/src/phrase-building/vps-reducer"; +import { + VpsReducerAction, + vpsReducer, +} from "../../../lib/src/phrase-building/vps-reducer"; import { getObjectSelection } from "../../../lib/src/phrase-building/blocks-utils"; import VPPicker from "./VPPicker"; import AllTensesDisplay from "./AllTensesDisplay"; @@ -34,6 +37,7 @@ function VPExplorer(props: { handleLinkClick: ((ts: number) => void) | "none"; entryFeeder: T.EntryFeeder; onlyPhrases?: boolean; + eventEmitter?: (e: string) => void; }) { const [vps, adjustVps] = useStickyReducer( vpsReducer, @@ -85,7 +89,7 @@ function VPExplorer(props: { const VPSFromUrl = getVPSFromUrl(); if (VPSFromUrl) { setMode("phrases"); - adjustVps({ + eventWrapper(adjustVps)({ type: "load vps", payload: VPSFromUrl, }); @@ -93,7 +97,15 @@ function VPExplorer(props: { // eslint-disable-next-line }, []); function handleSubjObjSwap() { - adjustVps({ type: "swap subj/obj" }); + eventWrapper(adjustVps)({ type: "swap subj/obj" }); + } + function eventWrapper(f: (a: VpsReducerAction) => void) { + return function (action: VpsReducerAction) { + if (props.eventEmitter) { + props.eventEmitter(`VP exlorer ${props.verb.entry.p}`); + } + return f(action); + }; } function quizLock(f: T) { if (mode === "quiz") { @@ -105,7 +117,7 @@ function VPExplorer(props: { return f; } function handleSetForm(form: T.FormVersion) { - adjustVps({ + eventWrapper(adjustVps)({ type: "set form", payload: form, }); @@ -134,7 +146,7 @@ function VPExplorer(props: {
@@ -190,13 +202,19 @@ function VPExplorer(props: { adjustVps({ type: "load vps", payload })} + onChange={(payload) => + eventWrapper(adjustVps)({ type: "load vps", payload }) + } vps={vps} /> )} {mode !== "phrases" && (
- +
)} {mode === "phrases" && ( @@ -207,7 +225,7 @@ function VPExplorer(props: { object={object} VS={vps.verb} opts={props.opts} - onChange={adjustVps} + onChange={eventWrapper(adjustVps)} /> )} {mode === "quiz" && } diff --git a/src/demo-components/VPBuilderDemo.tsx b/src/demo-components/VPBuilderDemo.tsx index aac23ac..60679a4 100644 --- a/src/demo-components/VPBuilderDemo.tsx +++ b/src/demo-components/VPBuilderDemo.tsx @@ -5,199 +5,221 @@ import Phonetics from "../components/src/Phonetics"; import { getVerbInfo } from "../lib/src/verb-info"; import verbs from "../verbs"; import { useStickyState } from "../components/library"; -import { - clamp -} from "../lib/src/p-text-helpers"; -import { - randomNumber, -} from "../lib/src/misc-helpers"; +import { clamp } from "../lib/src/p-text-helpers"; +import { randomNumber } from "../lib/src/misc-helpers"; import { entryFeeder } from "./entryFeeder"; const transitivities: T.Transitivity[] = [ - "transitive", - "intransitive", - "grammatically transitive", + "transitive", + "intransitive", + "grammatically transitive", ]; -const allVerbs = verbs.map((v: { entry: T.DictionaryEntry, complement?: T.DictionaryEntry }) => ({ +const allVerbs = verbs.map( + (v: { entry: T.DictionaryEntry; complement?: T.DictionaryEntry }) => ({ verb: v, info: getVerbInfo(v.entry, v.complement), -})); + }) +); type VerbType = "simple" | "stative compound" | "dynamic compound"; const verbTypes: VerbType[] = [ - "simple", - "stative compound", - "dynamic compound", + "simple", + "stative compound", + "dynamic compound", ]; -function VPBuilderDemo({ opts }: { - opts: T.TextOptions, -}) { - const [verbTs, setVerbTs] = useStickyState(0, "verbTs1"); - const [verbTypeShowing, setVerbTypeShowing] = useStickyState("simple", "vTypeShowing"); - const [transitivityShowing, setTransitivityShowing] = useStickyState("intransitive", "transitivityShowing1"); - // const onlyGrammTrans = (arr: Transitivity[]) => ( - // arr.length === 1 && arr[0] === "grammatically transitive" - // ); - // const ensureSimpleVerbTypeSelected = () => { - // if (!verbTypesShowing.includes["simple"]) { - // setVerbTypesShowing([...verbTypesShowing, "simple"]); - // } - // } - const handleVerbIndexChange = (e: any) => { - setVerbTs(parseInt(e.target.value)); +function VPBuilderDemo({ opts }: { opts: T.TextOptions }) { + const [verbTs, setVerbTs] = useStickyState(0, "verbTs1"); + const [verbTypeShowing, setVerbTypeShowing] = useStickyState( + "simple", + "vTypeShowing" + ); + const [transitivityShowing, setTransitivityShowing] = + useStickyState("intransitive", "transitivityShowing1"); + // const onlyGrammTrans = (arr: Transitivity[]) => ( + // arr.length === 1 && arr[0] === "grammatically transitive" + // ); + // const ensureSimpleVerbTypeSelected = () => { + // if (!verbTypesShowing.includes["simple"]) { + // setVerbTypesShowing([...verbTypesShowing, "simple"]); + // } + // } + const handleVerbIndexChange = (e: any) => { + setVerbTs(parseInt(e.target.value)); + }; + const handleTypeSelection = (e: any) => { + const type = e.target.value as VerbType; + if (type === "dynamic compound") { + setTransitivityShowing("transitive"); } - const handleTypeSelection = (e: any) => { - const type = e.target.value as VerbType; - if (type === "dynamic compound") { - setTransitivityShowing("transitive"); - } - if (type === "stative compound" && transitivityShowing === "grammatically transitive") { - setTransitivityShowing("transitive"); - } - setVerbTypeShowing(type); + if ( + type === "stative compound" && + transitivityShowing === "grammatically transitive" + ) { + setTransitivityShowing("transitive"); } - const handleTransitivitySelection = (e: any) => { - const transitivity = e.target.value as T.Transitivity; - if (transitivity === "grammatically transitive") { - setVerbTypeShowing("simple"); - } - if (transitivity === "intransitive" && verbTypeShowing === "dynamic compound") { - setTransitivityShowing("transitive"); - return; - } - setTransitivityShowing(e.target.value as T.Transitivity); + setVerbTypeShowing(type); + }; + const handleTransitivitySelection = (e: any) => { + const transitivity = e.target.value as T.Transitivity; + if (transitivity === "grammatically transitive") { + setVerbTypeShowing("simple"); } - const verbsAvailable = allVerbs.filter((verb) => ( - ( - (verb.info.type === "transitive or grammatically transitive simple" && verbTypeShowing === "simple") && (transitivityShowing === "transitive" || transitivityShowing === "grammatically transitive") - ) || - (( - verbTypeShowing === verb.info.type || - (verbTypeShowing === "stative compound" && verb.info.type === "dynamic or stative compound") || - (verbTypeShowing === "dynamic compound" && verb.info.type === "dynamic or stative compound") || - (verbTypeShowing === "dynamic compound" && verb.info.type === "dynamic or generative stative compound") || - (verbTypeShowing === "stative compound" && verb.info.type === "dynamic or generative stative compound") - ) - && ( - transitivityShowing === verb.info.transitivity - )) - )).sort((a, b) => a.verb.entry.p.localeCompare(b.verb.entry.p, "ps")); + if ( + transitivity === "intransitive" && + verbTypeShowing === "dynamic compound" + ) { + setTransitivityShowing("transitive"); + return; + } + setTransitivityShowing(e.target.value as T.Transitivity); + }; + const verbsAvailable = allVerbs + .filter( + (verb) => + (verb.info.type === "transitive or grammatically transitive simple" && + verbTypeShowing === "simple" && + (transitivityShowing === "transitive" || + transitivityShowing === "grammatically transitive")) || + ((verbTypeShowing === verb.info.type || + (verbTypeShowing === "stative compound" && + verb.info.type === "dynamic or stative compound") || + (verbTypeShowing === "dynamic compound" && + verb.info.type === "dynamic or stative compound") || + (verbTypeShowing === "dynamic compound" && + verb.info.type === "dynamic or generative stative compound") || + (verbTypeShowing === "stative compound" && + verb.info.type === "dynamic or generative stative compound")) && + transitivityShowing === verb.info.transitivity) + ) + .sort((a, b) => a.verb.entry.p.localeCompare(b.verb.entry.p, "ps")); - const v = (() => { - const vFound = verbsAvailable.find(v => v.verb.entry.ts === verbTs); - if (vFound) return vFound; - if (verbsAvailable.length === 0) return undefined; - const vTopOfList = verbsAvailable[0]; - setVerbTs(vTopOfList.verb.entry.ts); - return vTopOfList; - })(); + const v = (() => { + const vFound = verbsAvailable.find((v) => v.verb.entry.ts === verbTs); + if (vFound) return vFound; + if (verbsAvailable.length === 0) return undefined; + const vTopOfList = verbsAvailable[0]; + setVerbTs(vTopOfList.verb.entry.ts); + return vTopOfList; + })(); - const pickRandomVerb = () => { - let newIndex: number; - do { - newIndex = randomNumber(0, verbsAvailable.length); - } while(verbsAvailable[newIndex].verb.entry.ts === verbTs); - setVerbTs(verbsAvailable[newIndex].verb.entry.ts); - }; - const makeVerbLabel = (entry: T.DictionaryEntry): string => ( - `${entry.p} - ${clamp(entry.e, 20)}` - ); - // const rs = v ? getAllRs(v.verb as T.VerbEntry) : undefined - return
-
-
-
-
- {v ? -
-
Select a verb:
-
- -
- -
-
-
-
- - {v.verb.entry} - {` `}-{` `} - {v.verb.entry} - - {` `} - {v.verb.entry.c} -
-
{v.verb.entry.e}
-
-
- :
- No such verbs available... -
- } + const pickRandomVerb = () => { + let newIndex: number; + do { + newIndex = randomNumber(0, verbsAvailable.length); + } while (verbsAvailable[newIndex].verb.entry.ts === verbTs); + setVerbTs(verbsAvailable[newIndex].verb.entry.ts); + }; + const makeVerbLabel = (entry: T.DictionaryEntry): string => + `${entry.p} - ${clamp(entry.e, 20)}`; + // const rs = v ? getAllRs(v.verb as T.VerbEntry) : undefined + return ( +
+
+
+
+
+ {v ? ( +
+
Select a verb:
+
+ +
+
-
-
Verb type:
-
- {verbTypes.map((type) => ( -
- null} - /> - -
- ))} -
-
Transitivity:
-
- {transitivities.map((transitivity) => ( -
- null} - value={transitivity} - /> - -
- ))} -
+
+
+
+ + {v.verb.entry} + {` `}-{` `} + {v.verb.entry} + + {` `} + {v.verb.entry.c}
+
{v.verb.entry.e}
+
+ ) : ( +
+ No such verbs available... +
+ )}
+
+
Verb type:
+
+ {verbTypes.map((type) => ( +
+ null} + /> + +
+ ))} +
+
Transitivity:
+
+ {transitivities.map((transitivity) => ( +
+ null} + value={transitivity} + /> + +
+ ))} +
+
+
- {v?.verb.entry &&
- -
} -
; +
+ {v?.verb.entry && ( +
+ +
+ )} +
+ ); } -export default VPBuilderDemo; \ No newline at end of file +export default VPBuilderDemo; diff --git a/src/lib/package.json b/src/lib/package.json index 846ae48..e77217d 100644 --- a/src/lib/package.json +++ b/src/lib/package.json @@ -1,6 +1,6 @@ { "name": "@lingdocs/inflect", - "version": "7.0.9", + "version": "7.0.11", "description": "Pashto inflector library", "main": "dist/index.js", "types": "dist/lib/library.d.ts", diff --git a/src/lib/src/parsing/parse-blocks.ts b/src/lib/src/parsing/parse-blocks.ts index 8574d27..09ae872 100644 --- a/src/lib/src/parsing/parse-blocks.ts +++ b/src/lib/src/parsing/parse-blocks.ts @@ -4,9 +4,9 @@ import { parseEquative } from "./parse-equative"; import { parseKidsSection } from "./parse-kids-section"; import { parseNeg } from "./parse-negative"; import { parseNPAP } from "./parse-npap"; -import { parsePastPart } from "./parse-past-part"; +import { parseVBP } from "./parse-vbp"; import { parsePH } from "./parse-ph"; -import { parseVerb } from "./parse-verb"; +import { parseVBE } from "./parse-vbe"; import { bindParseResult, returnParseResult, @@ -14,6 +14,7 @@ import { isParsedVBP, startsVerbSection, } from "./utils"; +import { isKedulStatEntry } from "./parse-verb-helpers"; export function parseBlocks( tokens: Readonly, @@ -32,14 +33,15 @@ export function parseBlocks( (b): b is T.ParsedPH => b.type === "PH" ); + // TOOD: rather parse VBP / VBE const allBlocks: T.ParseResult[] = [ ...(!inVerbSection ? parseNPAP(tokens, lookup) : []), // ensure at most one of each PH, VBE, VBP ...(prevPh ? [] : parsePH(tokens)), ...(blocks.some(isParsedVBE) ? [] - : [...parseVerb(tokens, lookup), ...parseEquative(tokens)]), - ...(blocks.some(isParsedVBP) ? [] : parsePastPart(tokens, lookup)), + : [...parseVBE(tokens, lookup), ...parseEquative(tokens)]), + ...(blocks.some(isParsedVBP) ? [] : parseVBP(tokens, lookup)), ...(blocks.some((b) => b.type === "negative") ? [] : parseNeg(tokens)), ...parseKidsSection(tokens, []), ]; @@ -82,12 +84,24 @@ function phMatches( if (!ph) { return true; } + if (!vb) { return true; } if (vb.info.type !== "verb") { return false; } + if (["را", "در", "ور"].includes(ph?.s)) { + if ( + isKedulStatEntry(vb.info.verb.entry) && + vb.info.base === "stem" && + vb.info.aspect === "perfective" + ) { + return true; + } + // TODO: handle را غل etc! ? or is + return false; + } const verbPh = getPhFromVerb(vb.info.verb, vb.info.base); return verbPh === ph.s; } diff --git a/src/lib/src/parsing/parse-kedul.test.ts b/src/lib/src/parsing/parse-kedul.test.ts new file mode 100644 index 0000000..64c0d8e --- /dev/null +++ b/src/lib/src/parsing/parse-kedul.test.ts @@ -0,0 +1,245 @@ +/* eslint-disable jest/valid-title */ +import * as T from "../../../types"; +import { parseKedul } from "./parse-kedul"; +import { kedulDyn, kedulStat } from "./irreg-verbs"; +import { tokenizer } from "./tokenizer"; +import { getPeople, removeKeys } from "./utils"; + +const tests: { + label: string; + cases: { + input: string; + output: T.ParsedVBE[]; + }[]; +}[] = [ + { + label: "keG-", + cases: [ + { + input: "کېږې", + output: getPeople(2, "sing").flatMap((person) => [ + { + type: "VB", + info: { + aspect: "imperfective", + base: "stem", + type: "verb", + verb: kedulStat, + }, + person, + }, + { + type: "VB", + info: { + aspect: "imperfective", + base: "stem", + type: "verb", + verb: kedulDyn, + }, + person, + }, + ]), + }, + ], + }, + { + label: "ked-", + cases: [ + { + input: "کېدم", + output: getPeople(1, "sing").flatMap((person) => [ + { + type: "VB", + info: { + aspect: "imperfective", + base: "root", + type: "verb", + verb: kedulStat, + }, + person, + }, + { + type: "VB", + info: { + aspect: "imperfective", + base: "root", + type: "verb", + verb: kedulDyn, + }, + person, + }, + ]), + }, + { + input: "کېدل", + output: [ + { + type: "VB", + info: { + aspect: "imperfective", + base: "root", + type: "verb", + verb: kedulStat, + }, + person: T.Person.ThirdPlurMale, + }, + { + type: "VB", + info: { + aspect: "imperfective", + base: "root", + type: "verb", + verb: kedulDyn, + }, + person: T.Person.ThirdPlurMale, + }, + ], + }, + { + input: "کېدلل", + output: [], + }, + ], + }, + { + label: "sh-", + cases: [ + { + input: "شې", + output: getPeople(2, "sing").flatMap((person) => [ + { + type: "VB", + info: { + aspect: "perfective", + base: "stem", + type: "verb", + verb: kedulStat, + }, + person, + }, + { + type: "VB", + info: { + aspect: "perfective", + base: "stem", + type: "verb", + verb: kedulDyn, + }, + person, + }, + ]), + }, + ], + }, + { + label: "shw-", + cases: [ + { + input: "شوم", + output: getPeople(1, "sing").flatMap((person) => [ + { + type: "VB", + info: { + aspect: "perfective", + base: "root", + type: "verb", + verb: kedulStat, + }, + person, + }, + { + type: "VB", + info: { + aspect: "perfective", + base: "root", + type: "verb", + verb: kedulDyn, + }, + person, + }, + ]), + }, + { + input: "شول", + output: [ + { + type: "VB", + info: { + aspect: "perfective", + base: "root", + type: "verb", + verb: kedulStat, + }, + person: T.Person.ThirdPlurMale, + }, + { + type: "VB", + info: { + aspect: "perfective", + base: "root", + type: "verb", + verb: kedulDyn, + }, + person: T.Person.ThirdPlurMale, + }, + ], + }, + { + input: "شولل", + output: [], + }, + ], + }, + { + label: "sho", + cases: [ + { + input: "شو", + output: [ + ...[ + T.Person.ThirdSingMale, + T.Person.FirstPlurMale, + T.Person.FirstPlurFemale, + ].flatMap((person) => + [kedulStat, kedulDyn].map((verb) => ({ + type: "VB", + info: { + aspect: "perfective", + base: "root", + type: "verb", + verb, + }, + person, + })) + ), + ...[T.Person.FirstPlurMale, T.Person.FirstPlurFemale].flatMap( + (person) => + [kedulStat, kedulDyn].map((verb) => ({ + type: "VB", + info: { + aspect: "perfective", + base: "stem", + type: "verb", + verb, + }, + person, + })) + ), + ], + }, + ], + }, +]; + +tests.forEach(({ label, cases }) => { + test(label, () => { + cases.forEach(({ input, output }) => { + const tokens = tokenizer(input); + const vbe = parseKedul(tokens); + expect(vbe.every((v) => v.errors.length === 0)).toBe(true); + expect(removeKeys(vbe.map((v) => v.body))).toIncludeSameMembers( + removeKeys(output) + ); + }); + }); +}); diff --git a/src/lib/src/parsing/parse-kedul.ts b/src/lib/src/parsing/parse-kedul.ts new file mode 100644 index 0000000..34694e4 --- /dev/null +++ b/src/lib/src/parsing/parse-kedul.ts @@ -0,0 +1,154 @@ +import * as T from "../../../types"; +import { getVerbEnding } from "./parse-verb-helpers"; +import { kedulDyn, kedulStat } from "./irreg-verbs"; +import { returnParseResults } from "./utils"; + +export function parseKedul( + tokens: Readonly +): T.ParseResult[] { + const [first, ...rest] = tokens; + const start = first.s.slice(0, -1); + const ending = first.s.at(-1) || ""; + const people = getVerbEnding(ending); + if (first.s === "شو") { + return returnParseResults(rest, [ + ...[ + T.Person.ThirdSingMale, + T.Person.FirstPlurMale, + T.Person.FirstPlurFemale, + ].flatMap((person) => + [kedulStat, kedulDyn].map((verb) => ({ + type: "VB", + info: { + aspect: "perfective", + base: "root", + type: "verb", + verb, + }, + person, + })) + ), + ...[T.Person.FirstPlurMale, T.Person.FirstPlurFemale].flatMap((person) => + [kedulStat, kedulDyn].map((verb) => ({ + type: "VB", + info: { + aspect: "perfective", + base: "stem", + type: "verb", + verb, + }, + person, + })) + ), + ]); + } + if (start === "کېږ") { + return returnParseResults( + rest, + people.stem.flatMap((person) => [ + { + type: "VB", + info: { + aspect: "imperfective", + base: "stem", + type: "verb", + verb: kedulStat, + }, + person, + }, + { + type: "VB", + info: { + aspect: "imperfective", + base: "stem", + type: "verb", + verb: kedulDyn, + }, + person, + }, + ]) + ); + } + if (start === "کېد" || (start === "کېدل" && ending !== "ل")) { + return returnParseResults( + rest, + people.root.flatMap((person) => [ + { + type: "VB", + info: { + aspect: "imperfective", + base: "root", + type: "verb", + verb: kedulStat, + }, + person, + }, + { + type: "VB", + info: { + aspect: "imperfective", + base: "root", + type: "verb", + verb: kedulDyn, + }, + person, + }, + ]) + ); + } + if (start === "ش") { + return returnParseResults( + rest, + people.stem.flatMap((person) => [ + { + type: "VB", + info: { + aspect: "perfective", + base: "stem", + type: "verb", + verb: kedulStat, + }, + person, + }, + { + type: "VB", + info: { + aspect: "perfective", + base: "stem", + type: "verb", + verb: kedulDyn, + }, + person, + }, + ]) + ); + } + if (start === "شو" || (start === "شول" && ending !== "ل")) { + return returnParseResults( + rest, + people.root.flatMap((person) => [ + { + type: "VB", + info: { + aspect: "perfective", + base: "root", + type: "verb", + verb: kedulStat, + }, + person, + }, + { + type: "VB", + info: { + aspect: "perfective", + base: "root", + type: "verb", + verb: kedulDyn, + }, + person, + }, + ]) + ); + } + return []; +} diff --git a/src/lib/src/parsing/parse-verb.test.ts b/src/lib/src/parsing/parse-vbe.test.ts similarity index 94% rename from src/lib/src/parsing/parse-verb.test.ts rename to src/lib/src/parsing/parse-vbe.test.ts index eb13146..ec96eb0 100644 --- a/src/lib/src/parsing/parse-verb.test.ts +++ b/src/lib/src/parsing/parse-vbe.test.ts @@ -9,7 +9,7 @@ import { raatlul, } from "./irreg-verbs"; import { lookup, wordQuery } from "./lookup"; -import { parseVerb } from "./parse-verb"; +import { parseVBE } from "./parse-vbe"; import { tokenizer } from "./tokenizer"; import { getPeople, removeKeys } from "./utils"; @@ -935,9 +935,6 @@ const tests: { }, ], }, - // TODO: It would probably be more effecient just to return the kedul verb options - // and then when we put things together with the perfective head parsed they could - // become raatlul etc... { input: "شي", output: [ @@ -965,42 +962,6 @@ const tests: { }, verb: kedulStat, }, - { - stem: { - persons: [ - T.Person.ThirdSingMale, - T.Person.ThirdSingFemale, - T.Person.ThirdPlurMale, - T.Person.ThirdPlurFemale, - ], - aspects: ["perfective"], - }, - verb: raatlul, - }, - { - stem: { - persons: [ - T.Person.ThirdSingMale, - T.Person.ThirdSingFemale, - T.Person.ThirdPlurMale, - T.Person.ThirdPlurFemale, - ], - aspects: ["perfective"], - }, - verb: dartlul, - }, - { - stem: { - persons: [ - T.Person.ThirdSingMale, - T.Person.ThirdSingFemale, - T.Person.ThirdPlurMale, - T.Person.ThirdPlurFemale, - ], - aspects: ["perfective"], - }, - verb: wartlul, - }, ], }, ], @@ -1011,7 +972,7 @@ tests.forEach(({ label, cases }) => { test(label, () => { cases.forEach(({ input, output }) => { const tokens = tokenizer(input); - const vbs = parseVerb(tokens, lookup).map((r) => r.body); + const vbs = parseVBE(tokens, lookup).map((r) => r.body); const madeVbsS = output.reduce((acc, o) => { return [ ...acc, diff --git a/src/lib/src/parsing/parse-verb.ts b/src/lib/src/parsing/parse-vbe.ts similarity index 80% rename from src/lib/src/parsing/parse-verb.ts rename to src/lib/src/parsing/parse-vbe.ts index 2ab9ae8..0c48b52 100644 --- a/src/lib/src/parsing/parse-verb.ts +++ b/src/lib/src/parsing/parse-vbe.ts @@ -1,26 +1,22 @@ import * as T from "../../../types"; import { removeFVarientsFromVerb } from "../accent-and-ps-utils"; import { isInVarients, lastVowelNotA } from "../p-text-helpers"; -import { - dartlul, - kedulDyn, - kedulStat, - raatlul, - tlul, - wartlul, -} from "./irreg-verbs"; +import { dartlul, raatlul, tlul, wartlul } from "./irreg-verbs"; import { LookupFunction } from "./lookup"; import { shortVerbEndConsonant } from "./misc"; +import { parseKedul } from "./parse-kedul"; +import { getVerbEnding } from "./parse-verb-helpers"; // TODO: کول verbs! // check that aawu stuff is working // check oo`azmooy - // check څاته // laaRa shum etc - +// TODO: proper use of perfective with sh +// TODO: use of raa, dar, war with sh // TODO: هغه لاړ -export function parseVerb( +export function parseVBE( tokens: Readonly, lookup: LookupFunction ): T.ParseResult[] { @@ -36,6 +32,7 @@ export function parseVerb( errors: [], })); } + const kedulStat = parseKedul(tokens); const ending = first.s.at(-1) || ""; const people = getVerbEnding(ending); const imperativePeople = getImperativeVerbEnding(ending); @@ -46,11 +43,14 @@ export function parseVerb( // console.log({ verbs: JSON.stringify(verbs) }); // } // Then find out which ones match exactly and how - return matchVerbs(first.s, verbs, people, imperativePeople).map((body) => ({ - tokens: rest, - body, - errors: [], - })); + return [ + ...kedulStat, + ...matchVerbs(first.s, verbs, people, imperativePeople).map((body) => ({ + tokens: rest, + body, + errors: [], + })), + ]; } function matchVerbs( @@ -65,6 +65,9 @@ function matchVerbs( const w: T.ParsedVBE[] = []; const lEnding = s.endsWith("ل"); const base = s.endsWith("ل") ? s : s.slice(0, -1); + if (["کېږ", "کېد", "ش", "شو", "شول"].includes(base)) { + return []; + } const matchShortOrLong = (b: string, x: string) => { return b === x || (!lEnding && b === x.slice(0, -1)); }; @@ -317,61 +320,6 @@ function getImperativeVerbEnding(e: string): T.Person[] { return []; } -function getVerbEnding(e: string): { - stem: T.Person[]; - root: T.Person[]; -} { - if (e === "م") { - return { - root: [T.Person.FirstSingMale, T.Person.FirstSingFemale], - stem: [T.Person.FirstSingMale, T.Person.FirstSingFemale], - }; - } else if (e === "ې") { - return { - root: [ - T.Person.SecondSingMale, - T.Person.SecondSingFemale, - T.Person.ThirdPlurFemale, - ], - stem: [T.Person.SecondSingMale, T.Person.SecondSingFemale], - }; - } else if (e === "ي") { - return { - stem: [ - T.Person.ThirdSingMale, - T.Person.ThirdSingFemale, - T.Person.ThirdPlurMale, - T.Person.ThirdPlurFemale, - ], - root: [], - }; - } else if (e === "و") { - return { - root: [T.Person.FirstPlurMale, T.Person.FirstPlurFemale], - stem: [T.Person.FirstPlurMale, T.Person.FirstPlurFemale], - }; - } else if (e === "ئ") { - return { - root: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale], - stem: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale], - }; - } else if (e === "ه") { - return { - root: [T.Person.ThirdSingFemale], - stem: [], - }; - } else if (e === "ل") { - return { - root: [T.Person.ThirdPlurMale], - stem: [], - }; - } - return { - root: [], - stem: [], - }; -} - // TODO: could handle all sh- verbs for efficiencies sake function parseIrregularVerb(s: string): T.ParsedVBE[] { if (["ته", "راته", "ورته", "درته"].includes(s)) { @@ -394,38 +342,6 @@ function parseIrregularVerb(s: string): T.ParsedVBE[] { }, ]; } - if (s === "شو") { - return [ - ...[ - T.Person.ThirdSingMale, - T.Person.FirstPlurMale, - T.Person.FirstPlurFemale, - ].flatMap((person) => - [kedulStat, kedulDyn].map((verb) => ({ - type: "VB", - info: { - aspect: "perfective", - base: "root", - type: "verb", - verb, - }, - person, - })) - ), - ...[T.Person.FirstPlurMale, T.Person.FirstPlurFemale].flatMap((person) => - [kedulStat, kedulDyn].map((verb) => ({ - type: "VB", - info: { - aspect: "perfective", - base: "stem", - type: "verb", - verb, - }, - person, - })) - ), - ]; - } return []; } diff --git a/src/lib/src/parsing/parse-past-part.test.ts b/src/lib/src/parsing/parse-vbp.test.ts similarity index 96% rename from src/lib/src/parsing/parse-past-part.test.ts rename to src/lib/src/parsing/parse-vbp.test.ts index 8ba67b9..666a29b 100644 --- a/src/lib/src/parsing/parse-past-part.test.ts +++ b/src/lib/src/parsing/parse-vbp.test.ts @@ -1,7 +1,7 @@ import * as T from "../../../types"; import { lookup, wordQuery } from "./lookup"; import { tokenizer } from "./tokenizer"; -import { parsePastPart } from "./parse-past-part"; +import { parseVBP } from "./parse-vbp"; import { kawulDyn, kawulStat, kedulDyn, kedulStat } from "./irreg-verbs"; const leedul = wordQuery("لیدل", "verb"); @@ -178,7 +178,7 @@ describe("parsing past participles", () => { test(label, () => { cases.forEach(({ input, output }) => { const tokens = tokenizer(input); - const res = parsePastPart(tokens, lookup).map(({ body }) => body); + const res = parseVBP(tokens, lookup).map(({ body }) => body); expect(res).toEqual(output); }); }); diff --git a/src/lib/src/parsing/parse-past-part.ts b/src/lib/src/parsing/parse-vbp.ts similarity index 58% rename from src/lib/src/parsing/parse-past-part.ts rename to src/lib/src/parsing/parse-vbp.ts index c3857c0..0b75fab 100644 --- a/src/lib/src/parsing/parse-past-part.ts +++ b/src/lib/src/parsing/parse-vbp.ts @@ -2,13 +2,23 @@ import * as T from "../../../types"; import { LookupFunction } from "./lookup"; import { returnParseResult } from "./utils"; -export function parsePastPart( +export function parseVBP( tokens: Readonly, lookup: LookupFunction ): T.ParseResult[] { if (tokens.length === 0) { return []; } + return [ + ...parsePastPart(tokens, lookup), + // ...parseAbility(tokens), + ]; +} + +function parsePastPart( + tokens: Readonly, + lookup: LookupFunction +): T.ParseResult[] { const [{ s }, ...rest] = tokens; const ending: "ی" | "ي" | "ې" = s.at(-1) as "ی" | "ي" | "ې"; if (!ending || !["ی", "ي", "ې"].includes(ending)) { @@ -32,6 +42,34 @@ export function parsePastPart( .flatMap((m) => returnParseResult(rest, m)); } +// function parseAbility( +// tokens: Readonly, +// lookup: LookupFunction +// ): T.ParseResult[] { +// // TODO: keday +// if (tokens.length === 0) { +// return []; +// } +// const [{ s }, ...rest] = tokens; +// const start = s.endsWith("ای") +// ? s.slice(0, -2) +// : s.endsWith("ی") +// ? s.slice(0, 1) +// : ""; +// if (!start) return []; +// const matches = lookup(start, "ability"); +// return matches +// .map((verb) => ({ +// type: "VB", +// info: { +// type: "ability", +// verb, +// aspect: "perfective", // TODO GET ASPECT!! +// }, +// })) +// .flatMap((m) => returnParseResult(rest, m)); +// } + function endingGenderNum(ending: "ی" | "ي" | "ې"): T.GenderNumber[] { if (ending === "ی") { return [ diff --git a/src/lib/src/parsing/parse-verb-helpers.ts b/src/lib/src/parsing/parse-verb-helpers.ts new file mode 100644 index 0000000..dfb9160 --- /dev/null +++ b/src/lib/src/parsing/parse-verb-helpers.ts @@ -0,0 +1,60 @@ +import * as T from "../../../types"; + +export function isKedulStatEntry(v: T.VerbDictionaryEntry): boolean { + return v.p === "کېدل" && v.e === "to become _____"; +} + +export function getVerbEnding(e: string): { + stem: T.Person[]; + root: T.Person[]; +} { + if (e === "م") { + return { + root: [T.Person.FirstSingMale, T.Person.FirstSingFemale], + stem: [T.Person.FirstSingMale, T.Person.FirstSingFemale], + }; + } else if (e === "ې") { + return { + root: [ + T.Person.SecondSingMale, + T.Person.SecondSingFemale, + T.Person.ThirdPlurFemale, + ], + stem: [T.Person.SecondSingMale, T.Person.SecondSingFemale], + }; + } else if (e === "ي") { + return { + stem: [ + T.Person.ThirdSingMale, + T.Person.ThirdSingFemale, + T.Person.ThirdPlurMale, + T.Person.ThirdPlurFemale, + ], + root: [], + }; + } else if (e === "و") { + return { + root: [T.Person.FirstPlurMale, T.Person.FirstPlurFemale], + stem: [T.Person.FirstPlurMale, T.Person.FirstPlurFemale], + }; + } else if (e === "ئ") { + return { + root: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale], + stem: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale], + }; + } else if (e === "ه") { + return { + root: [T.Person.ThirdSingFemale], + stem: [], + }; + } else if (e === "ل") { + return { + root: [T.Person.ThirdPlurMale], + stem: [], + }; + } + return { + root: [], + stem: [], + }; +} diff --git a/src/lib/src/parsing/parse-vp.ts b/src/lib/src/parsing/parse-vp.ts index 52df3ab..f03f982 100644 --- a/src/lib/src/parsing/parse-vp.ts +++ b/src/lib/src/parsing/parse-vp.ts @@ -104,6 +104,7 @@ function getTenses( } if (verb.info.type === "verb") { if (verb.info.aspect === "perfective") { + // TODO: handle kedul sh perfective if (!ph) { return []; } diff --git a/src/lib/src/parsing/utils.ts b/src/lib/src/parsing/utils.ts index 1ed4943..01f814f 100644 --- a/src/lib/src/parsing/utils.ts +++ b/src/lib/src/parsing/utils.ts @@ -62,6 +62,18 @@ export function bindParseResult( return cleanOutResults(nextPossibilities); } +export function returnParseResults( + tokens: Readonly, + body: D[], + errors?: T.ParseError[] +): T.ParseResult[] { + return body.map>((b) => ({ + tokens, + body: b, + errors: errors || [], + })); +} + export function returnParseResultS( tokens: Readonly, body: D, diff --git a/src/lib/src/phrase-building/compile.ts b/src/lib/src/phrase-building/compile.ts index 722430c..8a61892 100644 --- a/src/lib/src/phrase-building/compile.ts +++ b/src/lib/src/phrase-building/compile.ts @@ -34,6 +34,8 @@ type BlankoutOptions = { predicate?: boolean; }; +// TODO: Should there be a way to get length options on compiled VP ? + // function compilePs({ blocks, kids, verb: { head, rest }, VP }: CompilePsInput): T.SingleOrLengthOpts { // if ("long" in rest) { // return { @@ -87,13 +89,13 @@ export function compileVP( export function compileVP( VP: T.VPRendered, form: T.FormVersion, - combineLengths: true, + combineLengths: boolean, blankOut?: BlankoutOptions ): { ps: T.PsString[]; e?: string[] }; export function compileVP( VP: T.VPRendered, form: T.FormVersion, - combineLengths?: true, + combineLengths?: boolean, blankOut?: BlankoutOptions ): { ps: T.SingleOrLengthOpts; e?: string[] } { // const verb = getVerbFromBlocks(VP.blocks).block; diff --git a/src/types.ts b/src/types.ts index a95d4f6..a04ea8e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -868,6 +868,14 @@ export type PossesorSelection = { np: NPSelection; }; +export type Noun = { + entry: NounEntry; + gender: Gender; + number: "sing" | "plur"; + adjectives: AdjectiveSelection[]; + possesor: undefined | Noun; +}; + // TODO require/import Person and PsString export type NounSelection = { type: "noun";