From 833f5acce6b709bec11fd2182836def81f2a2466 Mon Sep 17 00:00:00 2001 From: adueck Date: Sat, 1 Apr 2023 15:28:49 +0400 Subject: [PATCH] more work and testing on new verb engine --- src/components/src/jsx-map.tsx | 1 - src/demo-components/VPBuilderDemo.tsx | 3 + src/lib/src/accent-helpers.ts | 6 +- src/lib/src/render-verb.test.ts | 1761 +++++++++++++++++++++++++ src/lib/src/render-verb.ts | 218 ++- src/types.ts | 44 + 6 files changed, 1909 insertions(+), 124 deletions(-) create mode 100644 src/lib/src/render-verb.test.ts diff --git a/src/components/src/jsx-map.tsx b/src/components/src/jsx-map.tsx index 6bf5d63..af6879a 100644 --- a/src/components/src/jsx-map.tsx +++ b/src/components/src/jsx-map.tsx @@ -57,7 +57,6 @@ export function psJSXMap(ps: T.PsJSX, target: "p" | "f", dealWithString: (ps: T. }, }; } catch (e) { - console.error(e); throw new Error("error mapping out PsJSX - unbalanced trees"); } } diff --git a/src/demo-components/VPBuilderDemo.tsx b/src/demo-components/VPBuilderDemo.tsx index c656bc9..c932a75 100644 --- a/src/demo-components/VPBuilderDemo.tsx +++ b/src/demo-components/VPBuilderDemo.tsx @@ -47,10 +47,13 @@ const testVerbTenses: T.VerbTense[] = [ const testPerfectTenses: T.PerfectTense[] = [ "presentPerfect", + "futurePerfect", + "habitualPerfect", "pastPerfect", "subjunctivePerfect", "wouldBePerfect", "wouldHaveBeenPerfect", + "pastSubjunctivePerfect", ]; const testAbilityTenses: T.ModalTense[] = testVerbTenses.map(t => `${t}Modal`); diff --git a/src/lib/src/accent-helpers.ts b/src/lib/src/accent-helpers.ts index 613c9c7..066d149 100644 --- a/src/lib/src/accent-helpers.ts +++ b/src/lib/src/accent-helpers.ts @@ -84,7 +84,11 @@ export function accentFSylsOnNFromEnd(syls: string[] | string, n: number): strin } export function accentOnNFromEnd(ps: T.PsString, n: number): T.PsString { - const fSyls = splitUpSyllables(removeAccents(ps.f)); + const fNoAccents = removeAccents(ps.f); + const fSyls = splitUpSyllables(fNoAccents); + // TODO: enable this and fix the tests it breaks!!! + // don't add accent if only one syllable + // if (fSyls.length === 1) return makePsString(ps.p, fNoAccents); return makePsString( ps.p, accentFSylsOnNFromEnd(fSyls, n), diff --git a/src/lib/src/render-verb.test.ts b/src/lib/src/render-verb.test.ts new file mode 100644 index 0000000..e458377 --- /dev/null +++ b/src/lib/src/render-verb.test.ts @@ -0,0 +1,1761 @@ +import * as T from "../../types"; +import { renderVerb } from "./render-verb"; + +function vEntry(e: any, c?: any): T.VerbEntry { + return { + entry: e, + ...c ? { + c, + } : {}, + } as T.VerbEntry; +} + +const wahul = vEntry({"ts":1527815399,"i":15049,"p":"وهل","f":"wahul","g":"wahul","e":"to hit","r":4,"c":"v. trans.","tppp":"واهه","tppf":"waahu","ec":"hit,hits,hitting,hit,hit"}); +const achawul = vEntry({"ts":1527811872,"i":224,"p":"اچول","f":"achawul","g":"achawul","e":"to put, pour, drop, throw, put on","r":4,"c":"v. trans.","ec":"put,puts,putting,put,put"}); +const ganul = vEntry({"ts":1527812000,"i":11398,"p":"ګڼل","f":"gaNul, guNul","g":"gaNul,guNul","e":"to count, consider, reckon, suppose, assume","r":4,"c":"v. trans.","tppp":"ګاڼه","tppf":"gaaNu","ec":"deem"}); +const kawulStat = vEntry({"ts":1579015359582,"i":11030,"p":"کول","f":"kawul","g":"kawul","e":"to make ____ ____ (as in \"He's making me angry.\")","r":4,"c":"v. trans.","ssp":"کړ","ssf":"kR","prp":"کړل","prf":"kRul","pprtp":"کړی","pprtf":"kúRey","noOo":true,"ec":"make,makes,making,made,made"}); +const kawulDyn = vEntry({"ts":1527812752,"i":11031,"p":"کول","f":"kawul","g":"kawul","e":"to do (an action or activity)","r":4,"c":"v. trans./gramm. trans.","ssp":"وکړ","ssf":"óokR","prp":"وکړل","prf":"óokRul","pprtp":"کړی","pprtf":"kúRey","diacExcept":true,"ec":"do,does,doing,did,done"}); +const kedulStat = vEntry({"ts":1581086654898,"i":11100,"p":"کېدل","f":"kedul","g":"kedul","e":"to become _____","r":2,"c":"v. intrans.","ssp":"ش","ssf":"sh","prp":"شول","prf":"shwul","pprtp":"شوی","pprtf":"shúwey","noOo":true,"ec":"become"}); +const kedulDyn = vEntry({"ts":1527812754,"i":11101,"p":"کېدل","f":"kedul","g":"kedul","e":"to happen, occur","r":2,"c":"v. intrans.","ssp":"وش","ssf":"óosh","prp":"وشول","prf":"óoshwul","pprtp":"شوی","pprtf":"shúwey","diacExcept":true,"ec":"happen"}); +const raatlul = vEntry({"ts":1527815216,"i":6875,"p":"راتلل","f":"raatlúl","g":"raatlul","e":"to come","r":4,"c":"v. intrans.","psp":"راځ","psf":"raadz","ssp":"راش","ssf":"ráash","prp":"راغلل","prf":"ráaghlul","pprtp":"راغلی","pprtf":"raaghúley","tppp":"راغی","tppf":"ráaghey","noOo":true,"separationAtP":2,"separationAtF":3,"ec":"come,comes,coming,came,come"}); +const wartlul = vEntry({"ts":1585228579997,"i":14821,"p":"ورتلل","f":"wărtlul","g":"wartlul","e":"to come / go over to (third person or place)","r":4,"c":"v. intrans.","psp":"ورځ","psf":"wărdz","ssp":"ورش","ssf":"wársh","prp":"ورغلل","prf":"wárghlul","pprtp":"ورغلی","pprtf":"wărghúley","tppp":"ورغی","tppf":"wărghey","noOo":true,"separationAtP":2,"separationAtF":3,"ec":"come,comes,coming,came,come"}); +const tlul = vEntry({"ts":1527815348,"i":3791,"p":"تلل","f":"tlul","g":"tlul","e":"to go","r":4,"c":"v. intrans.","psp":"ځ","psf":"dz","ssp":"لاړ ش","ssf":"láaR sh","prp":"لاړ","prf":"láaR","ec":"go,goes,going,went,gone"}); +const awuxtul = vEntry({"ts":1527814012,"i":1133,"p":"اوښتل","f":"awUxtul","g":"awUxtul","e":"to pass over, overturn, be flipped over, spill over, shift, change, diverge, pass, cross, abandon; to be sprained","r":4,"c":"v. intrans.","psp":"اوړ","psf":"awR","ec":"pass","ep":"over"}); +const khorul = vEntry({"ts":1527812790,"i":6002,"p":"خوړل","f":"khoRul","g":"khoRul","e":"to eat, to bite","r":4,"c":"v. trans.","psp":"خور","psf":"khor","tppp":"خوړ","tppf":"khoR","ec":"eat,eats,eating,ate,eaten"}); +const azmoyul = vEntry({"ts":1527811605,"i":468,"p":"ازمویل","f":"azmoyul","g":"azmoyul","e":"to attempt, try; to experiment, test","r":4,"c":"v. trans.","sepOo":true,"ec":"try"}); +const khatul = vEntry({"ts":1527814025,"i":5677,"p":"ختل","f":"khatul","g":"khatul","e":"to climb, ascend, rise, go up; to fall out, to fall off, to leave/dissapear; to turn out to be ...; to give a sentence (in law)","r":3,"c":"v. intrans.","psp":"خېژ","psf":"khejz","tppp":"خوت","tppf":"khot","ec":"climb"}); +const rasedul = vEntry({"ts":1527813573,"i":7057,"p":"رسېدل","f":"rasedul","g":"rasedul","e":"arrive, reach; (fig.) understand, attain to; mature, ripen","r":4,"c":"v. intrans.","shortIntrans":true,"ec":"arrive"}); +const weshul = vEntry({"ts":1527811701,"i":15106,"p":"وېشل","f":"weshul","g":"weshul","e":"divide, distribute, share","r":4,"c":"v. trans.","ec":"divide"}); + +test("various tenses with simple verbs", () => { + const tests: { verb: T.VerbEntry, tense: T.VerbTense, person: T.Person, output: ReturnType }[] = [ + { + tense: "presentVerb", + verb: wahul, + person: 0, + output: { + hasBa: false, + verbBlocks: [ + { + type: "VA", + ps: [ + { + p: "وهم", + f: "wahum" + } + ], + person: 0 + }, + ], + }, + }, + { + tense: "subjunctiveVerb", + person: 3, + verb: wahul, + output: { + hasBa: false, + "verbBlocks": [ + { + "type": "PH", + "ps": { + "p": "و", + "f": "óo" + } + }, + { + "type": "VA", + "ps": [ + { + "p": "وهې", + "f": "wahe" + } + ], + "person": 3 + } + ], + }, + }, + { + tense: "perfectiveFuture", + person: 6, + verb: achawul, + output: { + "hasBa": true, + "verbBlocks": [ + { + "type": "PH", + "ps": { + "p": "وا", + "f": "wáa" + } + }, + { + "type": "VA", + "ps": [ + { + "p": "چوو", + "f": "chawoo" + } + ], + "person": 6 + } + ] + }, + }, + { + tense: "habitualPerfectivePast", + person: 8, + verb: achawul, + output: { + "hasBa": true, + "verbBlocks": [ + { + "type": "PH", + "ps": { + "p": "وا", + "f": "wáa" + } + }, + { + "type": "VA", + "ps": { + "long": [ + { + "p": "چولئ", + "f": "chawuleyy" + } + ], + "short": [ + { + "p": "چوئ", + "f": "chaweyy" + } + ] + }, + "person": 8 + } + ] + }, + }, + { + tense: "imperfectivePast", + person: 1, + verb: achawul, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "VA", + "ps": { + "long": [ + { + "p": "اچولم", + "f": "achawúlum" + } + ], + "short": [ + { + "p": "اچوم", + "f": "achawúm" + } + ] + }, + "person": 1 + } + ] + }, + }, + // don't do redundant ل on 3rd pers masc plural + { + tense: "imperfectivePast", + person: T.Person.ThirdPlurMale, + verb: achawul, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "VA", + "ps": { + "long": [ + { + "p": "اچول", + "f": "achawúl" + } + ], + "short": [ + { + "p": "اچول", + "f": "achawúl" + } + ] + }, + "person": 10 + } + ] + }, + }, + // expception tlul with inflecting perfective head in the non-past + { + tense: "subjunctiveVerb", + person: 1, + verb: tlul, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "PH", + "ps": { + "p": "لاړه ", + "f": "láaRa " + } + }, + { + "type": "VA", + "ps": [ + { + "p": "شم", + "f": "shum" + } + ], + "person": 1 + } + ] + }, + }, + // different accent with -yul ending verbs + { + verb: azmoyul, + tense: "imperfectivePast", + person: 0, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "VA", + "ps": { + "long": [ + { + "p": "ازمویلم", + "f": "azmóyulum" + } + ], + "short": [ + { + "p": "ازمویم", + "f": "azmóyum" + } + ] + }, + "person": 0 + } + ] + }, + }, + // intransitive -edul verbs + { + verb: rasedul, + tense: "presentVerb", + person: 4, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "VA", + "ps": { + "long": [ + { + "p": "رسېږي", + "f": "raséGee" + } + ], + "short": [ + { + "p": "رسي", + "f": "rasée" + } + ] + }, + "person": 4 + } + ] + }, + }, + ]; + tests.forEach(tn => { + expect(renderVerb({ + verb: tn.verb, + tense: tn.tense, + person: tn.person, + voice: "active", + })).toEqual(tn.output); + }); +}); + +test("idiosyncratic 3rd pers masc sing past endings", () => { + const tests: { verb: T.VerbEntry, tense: T.VerbTense, output: ReturnType }[] = [ + // basic -awul idiosyncricity "aawu" + { + tense: "imperfectivePast", + verb: achawul, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "VA", + "ps": { + "long": [ + { + "p": "اچولو", + "f": "achawúlo" + } + ], + "short": [ + { + "p": "اچاوه", + "f": "achaawú" + } + ] + }, + "person": 4 + } + ] + }, + }, + { + tense: "perfectivePast", + verb: wahul, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "PH", + "ps": { + "p": "و", + "f": "óo" + } + }, + { + "type": "VA", + "ps": { + "long": [ + { + "p": "وهلو", + "f": "wahulo" + } + ], + "short": [ + { + "p": "واهه", + "f": "waahu" + } + ] + }, + "person": 4 + } + ] + }, + }, + // words that have a specified special tppp like gaNul / gaaNu + { + tense: "imperfectivePast", + verb: ganul, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "VA", + "ps": { + "long": [ + { + "p": "ګڼلو", + "f": "gaNúlo" + } + ], + "short": [ + { + "p": "ګاڼه", + "f": "gaaNú" + } + ] + }, + "person": 4 + } + ] + }, + }, + // words who's specified special tppp ends in a consonant + { + verb: khatul, + tense: "imperfectivePast", + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "VA", + "ps": { + "long": [ + { + "p": "ختلو", + "f": "khatúlo" + } + ], + "short": [ + { + "p": "خوت", + "f": "khót" + }, + { + "p": "خوته", + "f": "khotú" + }, + { + "p": "خوتو", + "f": "khotó" + } + ] + }, + "person": 4 + } + ] + }, + }, + // verbs ending in a dental د or ت + { + verb: rasedul, + tense: "perfectivePast", + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "PH", + "ps": { + "p": "و", + "f": "óo" + } + }, + { + "type": "VA", + "ps": { + "long": [ + { + "p": "رسېدلو", + "f": "rasedulo" + } + ], + "short": [ + { + p: "رسېد", + f: "rased", + }, + { + "p": "رسېده", + "f": "rasedu" + }, + { + "p": "رسېدو", + "f": "rasedo" + } + ] + }, + "person": 4 + } + ] + } + }, + { + verb: rasedul, + tense: "habitualImperfectivePast", + output: { + "hasBa": true, + "verbBlocks": [ + { + "type": "VA", + "ps": { + "long": [ + { + "p": "رسېدلو", + "f": "rasedúlo" + } + ], + "short": [ + { + "p": "رسېد", + "f": "raséd" + }, + { + "p": "رسېده", + "f": "rasedú" + }, + { + "p": "رسېدو", + "f": "rasedó" + } + ] + }, + "person": 4 + } + ] + }, + }, + { + verb: awuxtul, + tense: "perfectivePast", + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "PH", + "ps": { + "p": "وا", + "f": "wáa" + } + }, + { + "type": "VA", + "ps": { + "long": [ + { + "p": "وښتلو", + "f": "wUxtulo" + } + ], + "short": [ + { + "p": "وښت", + "f": "wUxt" + }, + { + "p": "وښته", + "f": "wUxtu" + }, + { + "p": "وښتو", + "f": "wUxto" + } + ] + }, + "person": 4 + } + ] + }, + }, + // verbs ending in something else + { + verb: weshul, + tense: "perfectivePast", + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "PH", + "ps": { + "p": "و", + "f": "óo" + } + }, + { + "type": "VA", + "ps": { + "long": [ + { + "p": "وېشلو", + "f": "weshulo" + } + ], + "short": [ + { + "p": "وېشه", + "f": "weshu" + }, + { + "p": "وېشو", + "f": "wesho" + } + ] + }, + "person": 4 + } + ] + }, + }, + // irregular kedul verbs + { + tense: "perfectivePast", + verb: kedulStat, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "VA", + "ps": { + "long": [ + { + "p": "شولو", + "f": "shwulo" + } + ], + "short": [ + { + "p": "شو", + "f": "sho" + } + ] + }, + "person": 4 + } + ] + }, + }, + { + tense: "perfectivePast", + verb: kedulDyn, + output: { + "hasBa": false, + "verbBlocks": [ + { + type: "PH", + ps: { p: "و", f: "óo" }, + }, + { + "type": "VA", + "ps": { + "long": [ + { + "p": "شولو", + "f": "shwulo" + } + ], + "short": [ + { + "p": "شو", + "f": "sho" + } + ] + }, + "person": 4 + } + ], + }, + }, + // irregular kawul verbs + { + tense: "imperfectivePast", + verb: kawulStat, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "VA", + "ps": { + "long": [ + { + "p": "کولو", + "f": "kawúlo" + } + ], + "short": [ + { + "p": "کاوه", + "f": "kaawú" + } + ] + }, + "person": 4 + } + ] + }, + }, + { + tense: "perfectivePast", + verb: kawulStat, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "VA", + "ps": { + "long": [ + { + "p": "کړلو", + "f": "kRulo" + } + ], + "short": [ + { + "p": "کړ", + "f": "kuR" + }, + { + "p": "کړه", + "f": "kRu" + }, + { + "p": "کړو", + "f": "kRo" + } + ] + }, + "person": 4 + } + ] + }, + }, + { + tense: "habitualPerfectivePast", + verb: kawulDyn, + output: { + "hasBa": true, + "verbBlocks": [ + { + "type": "PH", + "ps": { + "p": "و", + "f": "óo" + } + }, + { + "type": "VA", + "ps": { + "long": [ + { + "p": "کړلو", + "f": "kRulo" + } + ], + "short": [ + { + "p": "کړ", + "f": "kuR" + }, + { + "p": "کړه", + "f": "kRu" + }, + { + "p": "کړو", + "f": "kRo" + } + ] + }, + "person": 4 + } + ], + }, + }, + // irregular tlul verbs + { + tense: "imperfectivePast", + verb: raatlul, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "VA", + "ps": { + "long": [ + { + "p": "راتللو", + "f": "raatlúlo" + } + ], + "short": [ + { + "p": "راته", + "f": "raatú" + }, + { + "p": "راتلو", + "f": "raatló" + } + ] + }, + "person": 4 + } + ] + }, + }, + { + tense: "imperfectivePast", + verb: wartlul, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "VA", + "ps": { + "long": [ + { + "p": "ورتللو", + "f": "wărtlúlo" + } + ], + "short": [ + { + "p": "ورته", + "f": "wărtú" + }, + { + "p": "ورتلو", + "f": "wărtló" + } + ] + }, + "person": 4 + } + ] + }, + }, + { + tense: "imperfectivePast", + verb: tlul, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "VA", + "ps": { + "long": [ + { + "p": "تللو", + "f": "tlúlo" + } + ], + "short": [ + { + p: "ته", + f: "tu", + }, + { + "p": "تلو", + "f": "tlo" + }, + ] + }, + "person": 4 + } + ] + }, + }, + { + tense: "perfectivePast", + verb: raatlul, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "PH", + "ps": { + "p": "را", + "f": "ráa" + } + }, + { + "type": "VA", + "ps": { + "long": [ + { + "p": "غللو", + "f": "ghlulo" + } + ], + "short": [ + { + "p": "غی", + "f": "ghey" + } + ] + }, + "person": 4 + } + ] + }, + }, + { + tense: "perfectivePast", + verb: wartlul, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "PH", + "ps": { + "p": "ور", + "f": "wár" + } + }, + { + "type": "VA", + "ps": { + "long": [ + { + "p": "غللو", + "f": "ghlulo" + } + ], + "short": [ + { + "p": "غی", + "f": "ghey" + } + ] + }, + "person": 4 + } + ] + }, + }, + ]; + tests.forEach(tn => { + expect(renderVerb({ + verb: tn.verb, + tense: tn.tense, + person: T.Person.ThirdSingMale, + voice: "active", + })).toEqual(tn.output); + }); +}); + +test("perfect simple verb forms", () => { + const tests: { verb: T.VerbEntry, tense: T.PerfectTense, person: T.Person, output: ReturnType }[] = [ + { + tense: "presentPerfect", + verb: wahul, + person: 1, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "PT", + "gender": "fem", + "number": "singular", + "ps": [ + { + "p": "وهلې", + "f": "wahúle" + } + ] + }, + { + "type": "EQ", + "person": 1, + "ps": [ + { + "p": "یم", + "f": "yum" + } + ] + } + ] + }, + }, + { + tense: "wouldBePerfect", + verb: wahul, + person: 2, + output: { + "hasBa": true, + "verbBlocks": [ + { + "type": "PT", + "gender": "masc", + "number": "singular", + "ps": [ + { + "p": "وهلی", + "f": "wahúley" + } + ] + }, + { + "type": "EQ", + "person": 2, + "ps": { + "long": [ + { + "p": "ولې", + "f": "wule" + } + ], + "short": [ + { + "p": "وې", + "f": "we" + } + ] + } + } + ] + }, + }, + { + tense: "pastPerfect", + verb: awuxtul, + person: 0, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "PT", + "gender": "masc", + "number": "singular", + "ps": { + "short": [ + { + "p": "اوښتی", + "f": "awÚxtey" + } + ], + "long": [ + { + "p": "اوښتلی", + "f": "awUxtúley" + } + ] + } + }, + { + "type": "EQ", + "person": 0, + "ps": { + "long": [ + { + "p": "ولم", + "f": "wulum" + } + ], + "short": [ + { + "p": "وم", + "f": "wum" + } + ] + } + } + ] + }, + }, + { + tense: "futurePerfect", + verb: raatlul, + person: 7, + output: { + "hasBa": true, + "verbBlocks": [ + { + "type": "PT", + "gender": "fem", + "number": "plural", + "ps": [ + { + "p": "راغلې", + "f": "raaghúle" + } + ] + }, + { + "type": "EQ", + "person": 7, + "ps": [ + { + "p": "یو", + "f": "yoo" + } + ] + } + ] + }, + }, + { + tense: "habitualPerfect", + verb: raatlul, + person: 10, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "PT", + "gender": "masc", + "number": "plural", + "ps": [ + { + "p": "راغلي", + "f": "raaghúlee" + } + ] + }, + { + "type": "EQ", + "person": 10, + "ps": [ + { + "p": "وي", + "f": "wee" + } + ] + } + ] + }, + }, + { + tense: "pastSubjunctivePerfect", + verb: ganul, + person: 2, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "PT", + "gender": "masc", + "number": "singular", + "ps": [ + { + "p": "ګڼلی", + "f": "gaNúley" + } + ] + }, + { + "type": "EQ", + "person": 2, + "ps": [ + { + "p": "وای", + "f": "waay" + }, + { + "p": "وی", + "f": "wey" + } + ] + } + ] + } + }, + { + tense: "subjunctivePerfect", + verb: ganul, + person: 2, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "PT", + "gender": "masc", + "number": "singular", + "ps": [ + { + "p": "ګڼلی", + "f": "gaNúley" + } + ] + }, + { + "type": "EQ", + "person": 2, + "ps": [ + { + "p": "وې", + "f": "we" + } + ] + } + ] + }, + }, + { + tense: "wouldBePerfect", + verb: ganul, + person: 6, + output: { + "hasBa": true, + "verbBlocks": [ + { + "type": "PT", + "gender": "masc", + "number": "plural", + "ps": [ + { + "p": "ګڼلي", + "f": "gaNúlee" + } + ] + }, + { + "type": "EQ", + "person": 6, + "ps": { + "long": [ + { + "p": "ولو", + "f": "wuloo" + } + ], + "short": [ + { + "p": "وو", + "f": "woo" + } + ] + } + } + ] + }, + }, + ]; + tests.forEach(tn => { + expect(renderVerb({ + verb: tn.verb, + tense: tn.tense, + person: tn.person, + voice: "active", + })).toEqual(tn.output); + }); +}); + +test("ability simple verb forms", () => { + const tests: { verb: T.VerbEntry, tense: T.ModalTense, person: T.Person, output: ReturnType }[] = [ + { + tense: "presentVerbModal", + verb: ganul, + person: 6, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "VI", + "ps": { + "long": [ + { + "p": "ګڼلی", + "f": "gaNúley" + }, + { + "p": "ګڼلای", + "f": "gaNúlaay" + } + ], + "short": [ + { + "p": "ګڼی", + "f": "gaNéy" + }, + { + "p": "ګڼای", + "f": "gaNáay" + } + ] + } + }, + { + "type": "VA", + "ps": [ + { + "p": "شو", + "f": "shoo" + } + ], + "person": 6 + } + ] + } + }, + { + tense: "subjunctiveVerbModal", + verb: ganul, + person: 0, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "PH", + "ps": { + "p": "و", + "f": "óo" + } + }, + { + "type": "VI", + "ps": { + "long": [ + { + "p": "ګڼلی", + "f": "gaNuley" + }, + { + "p": "ګڼلای", + "f": "gaNulaay" + } + ], + "short": [ + { + "p": "ګڼی", + "f": "gaNey" + }, + { + "p": "ګڼای", + "f": "gaNaay" + } + ] + } + }, + { + "type": "VA", + "ps": [ + { + "p": "شم", + "f": "shum" + } + ], + "person": 0 + } + ] + }, + }, + { + tense: "habitualPerfectivePastModal", + verb: ganul, + person: 0, + output: { + "hasBa": true, + "verbBlocks": [ + { + "type": "PH", + "ps": { + "p": "و", + "f": "óo" + } + }, + { + "type": "VI", + "ps": { + "long": [ + { + "p": "ګڼلی", + "f": "gaNuley" + }, + { + "p": "ګڼلای", + "f": "gaNulaay" + } + ], + "short": [ + { + "p": "ګڼی", + "f": "gaNey" + }, + { + "p": "ګڼای", + "f": "gaNaay" + } + ] + } + }, + { + "type": "VA", + "ps": { + "long": [ + { + "p": "شولم", + "f": "shwulum" + } + ], + "short": [ + { + "p": "شوم", + "f": "shwum" + } + ] + }, + "person": 0 + } + ] + }, + }, + // exception with tlul verbs losing aspect + { + tense: "subjunctiveVerbModal", + person: 10, + verb: tlul, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "VI", + "ps": { + "long": [ + { + "p": "تللی", + "f": "tlúley" + }, + { + "p": "تللای", + "f": "tlúlaay" + } + ], + "short": [ + { + "p": "تلی", + "f": "tléy" + }, + { + "p": "تلای", + "f": "tláay" + } + ] + } + }, + { + "type": "VA", + "ps": [ + { + "p": "شي", + "f": "shee" + } + ], + "person": 10 + } + ] + }, + } + ]; + tests.forEach(tn => { + expect(renderVerb({ + verb: tn.verb, + tense: tn.tense, + person: tn.person, + voice: "active", + })).toEqual(tn.output); + }); +}); + +test("passive simple verbs", () => { + const tests: { verb: T.VerbEntry, tense: T.VerbTense, person: T.Person, output: ReturnType }[] = [ + { + tense: "presentVerb", + verb: ganul, + person: 0, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "welded", + "left": { + "type": "VI", + "ps": [ + { + "p": "ګڼل", + "f": "gaNul" + } + ] + }, + "right": { + "type": "VA", + "ps": [ + { + "p": "کېږم", + "f": "kéGum" + } + ], + "person": 0 + } + } + ] + }, + }, + { + tense: "perfectivePast", + verb: khorul, + person: 7, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "PH", + "ps": { + "p": "و", + "f": "óo" + } + }, + { + "type": "welded", + "left": { + "type": "VI", + "ps": [ + { + "p": "خوړل", + "f": "khoRul" + } + ] + }, + "right": { + "type": "VA", + "ps": { + "long": [ + { + "p": "شولو", + "f": "shwuloo" + } + ], + "short": [ + { + "p": "شوو", + "f": "shwoo" + } + ] + }, + "person": 7 + } + } + ] + }, + } + ]; + tests.forEach(tn => { + expect(renderVerb({ + verb: tn.verb, + tense: tn.tense, + person: tn.person, + voice: "passive", + })).toEqual(tn.output); + }); +}); + +test("passive perfect simple verbs", () => { + const tests: { verb: T.VerbEntry, tense: T.PerfectTense, person: T.Person, output: ReturnType }[] = [ + { + tense: "presentPerfect", + verb: khorul, + person: 9, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "welded", + "left": { + "type": "VI", + "ps": [ + { + "p": "خوړل", + "f": "khoRul" + } + ] + }, + "right": { + "type": "PT", + "gender": "fem", + "number": "plural", + "ps": [ + { + "p": "شوې", + "f": "shúwe" + } + ] + } + }, + { + "type": "EQ", + "person": 9, + "ps": [ + { + "p": "یئ", + "f": "yeyy" + } + ] + } + ] + }, + }, + { + tense: "pastSubjunctivePerfect", + verb: khorul, + person: 10, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "welded", + "left": { + "type": "VI", + "ps": [ + { + "p": "خوړل", + "f": "khoRul" + } + ] + }, + "right": { + "type": "PT", + "gender": "masc", + "number": "plural", + "ps": [ + { + "p": "شوي", + "f": "shúwee" + } + ] + } + }, + { + "type": "EQ", + "person": 10, + "ps": [ + { + "p": "وای", + "f": "waay" + }, + { + "p": "وی", + "f": "wey" + } + ] + } + ] + }, + }, + ]; + tests.forEach(tn => { + expect(renderVerb({ + verb: tn.verb, + tense: tn.tense, + person: tn.person, + voice: "passive", + })).toEqual(tn.output); + }); +}); + +test("passive ability simple verbs", () => { + const tests: { verb: T.VerbEntry, tense: T.ModalTense, person: T.Person, output: ReturnType }[] = [ + { + verb: ganul, + tense: "presentVerbModal", + person: 4, + output: { + "hasBa": false, + "verbBlocks": [ + { + "type": "welded", + "left": { + "type": "VI", + "ps": [ + { + "p": "ګڼل", + "f": "gaNul" + } + ] + }, + "right": { + "type": "VI", + "ps": { + "long": [ + { + "p": "کېدلی", + "f": "kedúley" + }, + { + "p": "کېدلای", + "f": "kedúlaay" + } + ], + "short": [ + { + "p": "کېدی", + "f": "kedéy" + }, + { + "p": "کېدای", + "f": "kedáay" + } + ] + } + } + }, + { + "type": "VA", + "ps": [ + { + "p": "شي", + "f": "shee" + } + ], + "person": 4 + } + ] + }, + }, + // note: passive ability verbs lose aspect + { + verb: ganul, + tense: "habitualPerfectivePastModal", + person: 2, + output: { + "hasBa": true, + "verbBlocks": [ + { + "type": "welded", + "left": { + "type": "VI", + "ps": [ + { + "p": "ګڼل", + "f": "gaNul" + } + ] + }, + "right": { + "type": "VI", + "ps": { + "long": [ + { + "p": "کېدلی", + "f": "kedúley" + }, + { + "p": "کېدلای", + "f": "kedúlaay" + } + ], + "short": [ + { + "p": "کېدی", + "f": "kedéy" + }, + { + "p": "کېدای", + "f": "kedáay" + } + ] + } + } + }, + { + "type": "VA", + "ps": { + "long": [ + { + "p": "شولې", + "f": "shwule" + } + ], + "short": [ + { + "p": "شوې", + "f": "shwe" + } + ] + }, + "person": 2 + } + ] + }, + }, + ]; + tests.forEach(tn => { + expect(renderVerb({ + verb: tn.verb, + tense: tn.tense, + person: tn.person, + voice: "passive", + })).toEqual(tn.output); + }); +}); \ No newline at end of file diff --git a/src/lib/src/render-verb.ts b/src/lib/src/render-verb.ts index 466cb61..6e4c408 100644 --- a/src/lib/src/render-verb.ts +++ b/src/lib/src/render-verb.ts @@ -1,3 +1,4 @@ +import * as T from "../../types"; import { functionOnOptLengths, getPersonInflectionsKey, @@ -5,11 +6,11 @@ import { noPersInfs, personGender, personIsPlural, + personNumber, } from "./misc-helpers"; import { yulEndingInfinitive, } from "./p-text-helpers"; -import * as T from "../../types"; import { concatPsString, getLength, @@ -34,63 +35,9 @@ const kedulStatVerb: T.VerbEntry = { entry: {"ts":1581086654898,"i":11100,"p":"کېدل","f":"kedul","g":"kedul","e":"to become _____","r":2,"c":"v. intrans.","ssp":"ش","ssf":"sh","prp":"شول","prf":"shwul","pprtp":"شوی","pprtf":"shúwey","noOo":true,"ec":"become"} as T.VerbDictionaryEntry, }; -// export type RenderedVerbB = VerbRenderedBlock -// | PerfectiveHeadBlock -// | ModalVerbBlock -// | ModalVerbKedulPart -// | PerfectEquativeBlock -// | PerfectParticipleBlock; - -// export type PerfectiveHeadBlock = { type: "perfectiveHead", ps: PsString }; -// export type VerbRenderedBlock = { -// type: "verb", -// block: Omit & { -// hasBa: boolean, -// ps: SingleOrLengthOpts, -// person: Person, -// complementWelded: undefined | Rendered | Rendered, -// }, -// }; - -// TODO the welded block with passive is the same as the stative compounds - -type VB = PH | VA | VPlain | PT | EQ | Welded; - -type PH = { - type: "perfectiveHeadBlock", - ps: T.PsString, -}; - -type VA = { - type: "verbBlockWithAgreement", - ps: T.SingleOrLengthOpts, - person: T.Person, -}; - -type VPlain = { - type: "verbBlockWithoutAgreement", - ps: T.SingleOrLengthOpts, -}; - -type PT = { - type: "participleBlock", - ps: T.SingleOrLengthOpts, - inflection: T.PersonInflectionsField, -}; - -type EQ = { - type: "equativeBlock", - ps: T.SingleOrLengthOpts, - person: T.Person, -}; - -type Welded = { - type: "weldedBlock", - left: VPlain, // TODO - will get more complex with compounds - right: VA | PT | VPlain, -} - // TODO: problem with laaR - no perfective split +// TODO: are azmóyulum and wáayulo really not just azmoyúlum and waayúlo ? +// TODO: automatic 3rd person idiosyncronizing of raTul raaTu, shaRul, shaaRu, rasedul rased etc export function renderVerb({ verb, tense, person, voice }: { verb: T.VerbEntry, @@ -99,8 +46,10 @@ export function renderVerb({ verb, tense, person, voice }: { voice: T.Voice, }): { hasBa: boolean, - verbBlocks: VB[], + verbBlocks: T.VB[], } { + // TODO: check for transitivity with passive voice ?? + // WARNING: this only works with simple verbs const hasBa = tenseHasBa(tense); if (isPerfectTense(tense)) { @@ -115,27 +64,17 @@ export function renderVerb({ verb, tense, person, voice }: { const { perfectiveHead, rootStem } = getRootStem({ verb, aspect, isPast, isAbility, person, voice }); - const perfectiveHeadBlock: PH | undefined = perfectiveHead ? { - type: "perfectiveHeadBlock", + const perfectiveHeadBlock: T.PH | undefined = perfectiveHead ? { + type: "PH", // should only need this for tlul and Daaredul? ps: fromPersInfls(perfectiveHead, person), } : undefined; - // if (voice === "passive") { - // const kedulPart = getPassiveVerbBlocks(tense, person, aspect, rootStem); - // return { - // hasBa, - // // @ts-ignore - // verbBlocks: perfectiveHeadBlock - // ? [perfectiveHeadBlock, kedulPart] - // : [kedulPart], - // } - // } const ending = getEnding(person, isPast); if (isAbility) { - const [vb, shPart] = getAbilityVerbBlocks({ verb, isPast, person, rootStem, aspect, voice }); + const [vb, shPart] = getAbilityVerbBlocks({ verb, isPast, person, root: rootStem, aspect, voice }); return { hasBa, - verbBlocks: perfectiveHeadBlock + verbBlocks: (perfectiveHeadBlock && voice === "active") ? [perfectiveHeadBlock, vb, shPart] : [vb, shPart], }; @@ -150,8 +89,8 @@ export function renderVerb({ verb, tense, person, voice }: { ], }; } - const verbBlock: VA = { - type: "verbBlockWithAgreement", + const verbBlock: T.VA = { + type: "VA", ps: addEnding({ rootStem, ending, person, isPast, verb, aspect, }), @@ -169,7 +108,8 @@ function getPassiveVerbBlocks({ root, tense, person }: { root: T.SingleOrLengthOpts, tense: T.VerbTense, person: T.Person, -}): Welded { +}): T.Welded { + /* istanbul ignore next */ if (!("long" in root)) { throw new Error("should have length versions in roots for passive"); } @@ -178,33 +118,55 @@ function getPassiveVerbBlocks({ root, tense, person }: { tense, person, voice: "active", - }) as { hasBa: boolean, verbBlocks: [VA] }; + }) as { hasBa: boolean, verbBlocks: [T.VA] }; return weld( { - type: "verbBlockWithoutAgreement", + type: "VI", ps: [root.long], }, auxVerb, ); } -function getAbilityVerbBlocks({ verb, isPast, person, rootStem, aspect, voice }: { +function getAbilityVerbBlocks({ verb, isPast, person, root, aspect, voice }: { verb: T.VerbEntry, isPast: boolean, person: T.Person, - rootStem: T.SingleOrLengthOpts, + root: T.SingleOrLengthOpts, aspect: T.Aspect, voice: T.Voice, -}): VB[] { +}): T.VB[] { + /* istanbul ignore next */ + if (!("long" in root)) { + throw new Error("should have length versions in roots for passive"); + } const noPerfective = isTlulVerb(verb) || isKedul(verb); const shBlock = getAbilityShPart(isPast, person); + if (voice === "passive") { + return [ + weld( + { + type: "VI", + ps: [root.long], + }, + { + type: "VI", + ps: addAbilityTailsToRs({ + long: { p: "کېدل", f: "kedúl" }, + short: { p: "کېد", f: "ked" }, + }, aspect, true), + }, + ), + shBlock, + ]; + } // TODO: this is redundant, we did it in another part of the program? - const verbBlock: VPlain = { - type: "verbBlockWithoutAgreement", - ps: addAbilityTailsToRs(rootStem, aspect, noPerfective), + const verbBlock: T.VI = { + type: "VI", + ps: addAbilityTailsToRs(root, aspect, noPerfective), }; return [verbBlock, shBlock]; - function getAbilityShPart(isPast: boolean, person: T.Person): VA { + function getAbilityShPart(isPast: boolean, person: T.Person): T.VA { // TODO: optimized shortcut version of this const { verbBlocks: [shBlock] } = renderVerb({ verb: kedulStatVerb, @@ -213,10 +175,10 @@ function getAbilityVerbBlocks({ verb, isPast, person, rootStem, aspect, voice }: voice: "active", }) as { hasBa: boolean, - verbBlocks: [VA], + verbBlocks: [T.VA], }; return { - type: "verbBlockWithAgreement", + type: "VA", ps: shBlock.ps, person, }; @@ -228,25 +190,25 @@ function getPerfectBlocks({ verb, tense, person, voice }: { tense: T.PerfectTense, person: T.Person, voice: T.Voice, -}): VB[] { +}): T.VB[] { const vInfo = getVerbInfo(verb.entry) as T.SimpleVerbInfo; // TODO: put this in a seperate function? if (voice === "passive") { const [pt, eq] = getKedulStatPerfect(person, tense); - const passiveRoot: VPlain = { - type: "verbBlockWithoutAgreement", + const passiveRoot: T.VI = { + type: "VI", ps: [noPersInfs(vInfo.root.imperfective).long], }; - const welded: Welded = weld(passiveRoot, pt); + const welded: T.Welded = weld(passiveRoot, pt); return [welded, eq]; } const equative = equativeEndings[perfectTenseToEquative(tense)]; const [row, col] = getVerbBlockPosFromPerson(person); - const equativeBlock: EQ = { - type: "equativeBlock", + const equativeBlock: T.EQ = { + type: "EQ", person, ps: "long" in equative ? { long: equative.long[row][col], @@ -254,20 +216,18 @@ function getPerfectBlocks({ verb, tense, person, voice }: { } : equative[row][col], } - - - - const participleBlock: PT = { - type: "participleBlock", - inflection: getPersonInflectionsKey(person), + const participleBlock: T.PT = { + type: "PT", + gender: personGender(person), + number: personNumber(person), ps: chooseParticipleInflection(inflectYey(noPersInfs(vInfo.participle.past)), person) } return [participleBlock, equativeBlock]; } -function weld(left: VPlain, right: VA | PT | VPlain): Welded { +function weld(left: T.VI, right: T.VA | T.PT | T.VI): T.Welded { return { - type: "weldedBlock", + type: "welded", left: { ...left, ps: functionOnOptLengths(left.ps, removeAccents), @@ -348,15 +308,20 @@ function addEnding({ rootStem, ending, person, isPast, verb, aspect }:{ const endLong = getLength(end, "long"); const endShort = getLength(end, "short"); const shortForm = idiosyncratic3rdPast - ? ensure3rdPast(endShort, rs.short, verb) + ? ensure3rdPast(endShort, rs.short, verb, aspect) : endShort.map(e => concatPsString(rs.short, e)); + // TODO: this is a bit hacky + const rsLong = rs.long.f === "tlul" + ? { p: "تلل", f: "tlúl" } + : rs.long; return { - long: endLong.map(safeEndAdd(rs.long)), + long: endLong.map(safeEndAdd(rsLong)), short: aspect === "imperfective" ? applyImperfectiveShortAccent(shortForm, yulEndingInfinitive(removeFVarients(verb.entry))) : shortForm, }; } + /* istanbul ignore next */ if ("long" in end) { throw new Error("should not be using verb stems with long and short endings"); } @@ -382,8 +347,6 @@ function perfectTenseToEquative(t: T.PerfectTense): keyof typeof equativeEndings ? "past" : t === "pastSubjunctivePerfect" ? "pastSubjunctive" - : t === "subjunctivePerfect" - ? "subjunctive" : t === "wouldBePerfect" ? "past" : "subjunctive" @@ -396,18 +359,12 @@ function chooseParticipleInflection(pinf: T.SingleOrLengthOpts, aspect: T.Aspect, noPerfective: boolean): T.SingleOrLengthOpts { - if (!("long" in rs)) { - throw new Error("rootStem for ability verb should have short and long versions"); - } +function addAbilityTailsToRs(rs: T.LengthOptions, aspect: T.Aspect, noPerfective: boolean): T.SingleOrLengthOpts { const tails: T.PsString[] = [ { p: "ی", f: "ey" }, { p: "ای", f: "aay" }, @@ -428,12 +385,16 @@ function addAbilityTailsToRs(rs: T.SingleOrLengthOpts, aspect: T.Asp } function applyImperfectiveShortAccent(form: T.PsString[], yulEnding: boolean): T.PsString[] { + // TODO: remove this hack - get proper working thing for accentOnNFromEnd not adding accent on 1 syllable words + if (form[0].f === "tu") { + return form; + } return form.map(f => { return accentOnNFromEnd(f, yulEnding ? 1 : 0); }); } -function ensure3rdPast(ending: T.PsString[], rs: T.PsString, verb: T.VerbEntry): T.PsString[] { +function ensure3rdPast(ending: T.PsString[], rs: T.PsString, verb: T.VerbEntry, aspect: T.Aspect): T.PsString[] { if (isKedul(verb) && rs.p === "شو") { return [{ p: "شو", f: "sho" }]; } @@ -444,8 +405,18 @@ function ensure3rdPast(ending: T.PsString[], rs: T.PsString, verb: T.VerbEntry): { p: "کړو", f: "kRo" }, ]; } - if (isTlulVerb(verb) && rs.p === "غل") { - return [{ p: "غی", f: "ghey" }]; + if (isTlulVerb(verb)) { + if (aspect === "perfective") { + return [{ p: "غی", f: "ghey" }]; + } + const specialTuShort: T.PsString = { + p: rs.p.slice(0, -1) + "ه", + f: rs.f.slice(0, -1) + (rs.f === "tl" ? "u" : "ú"), + }; + return [ + specialTuShort, + concatPsString(rs, { p: "و", f: "o" }), + ]; } if (verb.entry.tppp && verb.entry.tppf) { const tip = makePsString(verb.entry.tppp, verb.entry.tppf) @@ -470,9 +441,12 @@ function ensure3rdPast(ending: T.PsString[], rs: T.PsString, verb: T.VerbEntry): const base = { p: rs.p.slice(0, -1), f: rs.f.slice(0, -2) }; return [concatPsString(base, { p: "اوه", f: "aawu" })]; } - - // nothing special or idiosyncratic needed for 3rd pers masc sing past - return ending.map(e => concatPsString(rs, e)); + const endsInDental = ["د", "ت"].includes(rs.p.slice(-1)); + // short endings like ورسېد + return (endsInDental ? [ + "", + ...ending, + ] : ending).map(e => concatPsString(rs, e)); } function getAspect(tense: T.VerbTense | T.ModalTense): T.Aspect { @@ -506,12 +480,12 @@ function ensureAccentLongStem(ps: T.PsString): T.PsString { }; } -function getKedulStatPerfect(person: T.Person, tense: T.PerfectTense): [PT, EQ] { +function getKedulStatPerfect(person: T.Person, tense: T.PerfectTense): [T.PT, T.EQ] { const { verbBlocks: [pt, eq] } = renderVerb({ verb: kedulStatVerb, tense, person, voice: "active", - }) as { hasBa: true, verbBlocks: [PT, EQ] }; + }) as { hasBa: true, verbBlocks: [T.PT, T.EQ] }; return [pt, eq]; } \ No newline at end of file diff --git a/src/types.ts b/src/types.ts index a9cefba..8d8e17e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1069,3 +1069,47 @@ export type MiniPronoun = { np: NPSelection, }; +export type VB = PH | VA | VI | PT | EQ | Welded; + +/** perfective head block */ +export type PH = { + type: "PH", + ps: PsString, +}; + +/** verb block with person agreement */ +export type VA = { + type: "VA", + ps: SingleOrLengthOpts, + person: Person, +}; + +/** invariable verb block */ +export type VI = { + type: "VI", + ps: SingleOrLengthOpts, +}; + +/** participle block with inflection */ +export type PT = { + type: "PT", + ps: SingleOrLengthOpts, + gender: Gender, + number: NounNumber, +}; + +/** equative block */ +export type EQ = { + type: "EQ", + ps: SingleOrLengthOpts, + person: Person, +}; + +/** a veb block with a welded part having it's accents removed */ +export type Welded = { + type: "welded", + /** accents must be removed from the left */ + left: VI, // TODO - will get more complex with compounds + right: VA | PT | VI, +}; +