more work on new verb renderer

This commit is contained in:
adueck 2023-06-15 20:10:41 +04:00
parent 878b39b654
commit bce8bf8365
19 changed files with 766 additions and 1005 deletions

View File

@ -9,14 +9,47 @@ A pashto inflection, verb conjugation, and phrase-generation engine, as well as
#### [Pashto Inflector Website/Demo](https://pashto-inflector.lingdocs.com)
This is **published as two libraries**:
## How it works
This library uses a 3-step process to generate gramattically correct Pashto phrases. This 3-step process corresponds directly to Chomsky's tripartrite structure for tranformational generative grammars.
| | Pashto Inflector Function | Chomskian Grammar Level |
|-|--------------------------| ----------------------- |
|1.| Assemble the phrase tree | Phrase Structure Rules |
|2.| Inflect the words in tree | Morphophonemic Rules |
|3.| Arrange the inflected words in order | Transformational Rules |
### 1. Assemble the phrase tree
**Phrase Structure Rules**
An abstract syntax tree is created with the various parts of a phrase represented as dictionary entries of the words as well as information about gender, number, and tense, etc. These are arranged in typed structures that allow for arranging NP and APs into verbal or equative phrases.
### 2. Inflect all the words in the phrase tree
**Morphophonemic Rules**
The dictionary entries for the words are inflected and conjugated as necessary.
### 3. Arrange the inflected words
**Transformational Rules**
The inflected words are arranged into possible phrases with various options for dropping or encliticizing pronouns, and placing the negation particle in the correct place(s).
![Phrase building process](./full-phrase-process.svg)
* The full process of phrase building
## Distributions
This is published on [a private NPM proxy registry](https://npm.lingdocs.com) as two libraries:
- @lingdocs/inflect
- `/src/components`
- `/src/lib`
- The core inflection engine with grammatical information and tools for processing LingDocs dictionary entries and Pashto text.
- Can be used with Node 16, as CommonJS
- @lingdocs/ps-react
- `/src/lib`
- `/src/components`
- @lingdocs/inflect plus react components for displaying Pashto text, phrase engine UI etc.
- Only available as an ES6 Module

1
full-phrase-process.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 88 KiB

View File

@ -38,31 +38,32 @@ function Block({ opts, block, king, script }: {
if (block.block.type === "negative") {
return <NegBlock opts={opts} imperative={block.block.imperative} script={script} />
}
if (block.block.type === "perfectiveHead") {
return <PerfHeadBlock opts={opts} ps={block.block.ps} script={script} />
}
if (block.block.type === "verb") {
return <VerbSBlock opts={opts} v={block.block.block} script={script} />;
}
if (block.block.type === "objectSelection") {
const role = king === "object" ? "king" : king === "subject" ? "servant" : undefined;
return <ObjectBlock opts={opts} obj={block.block.selection} role={role} script={script} />;
}
if (block.block.type === "perfectParticipleBlock") {
return <VerbSBlock opts={opts} v={block.block} script={script} />;
}
if (block.block.type === "perfectEquativeBlock") {
return <EquativeBlock opts={opts} eq={block.block} script={script} />;
}
if (block.block.type === "modalVerbBlock") {
return <ModalVerbBlock opts={opts} v={block.block} script={script} />;
}
if (block.block.type === "modalVerbKedulPart") {
return <ModalAuxBlock opts={opts} aux={block.block} script={script} />
}
if (block.block.type === "complement") {
return <ComplementBlock opts={opts} comp={block.block.selection} script={script} />;
}
return <div>TODO</div>
// if (block.block.type === "perfectiveHead") {
// return <PerfHeadBlock opts={opts} ps={block.block.ps} script={script} />
// }
// if (block.block.type === "verb") {
// return <VerbSBlock opts={opts} v={block.block.block} script={script} />;
// }
// if (block.block.type === "objectSelection") {
// const role = king === "object" ? "king" : king === "subject" ? "servant" : undefined;
// return <ObjectBlock opts={opts} obj={block.block.selection} role={role} script={script} />;
// }
// if (block.block.type === "perfectParticipleBlock") {
// return <VerbSBlock opts={opts} v={block.block} script={script} />;
// }
// if (block.block.type === "perfectEquativeBlock") {
// return <EquativeBlock opts={opts} eq={block.block} script={script} />;
// }
// if (block.block.type === "modalVerbBlock") {
// return <ModalVerbBlock opts={opts} v={block.block} script={script} />;
// }
// if (block.block.type === "modalVerbKedulPart") {
// return <ModalAuxBlock opts={opts} aux={block.block} script={script} />
// }
// if (block.block.type === "complement") {
// return <ComplementBlock opts={opts} comp={block.block.selection} script={script} />;
// }
return null;
}
@ -80,96 +81,96 @@ function Border({ children, extraClassName, padding }: { children: JSX.Element |
</div>
}
function VerbSBlock({ opts, v, script }: {
opts: T.TextOptions,
script: "p" | "f",
v: T.VerbRenderedBlock["block"] | T.PerfectParticipleBlock,
}) {
const [length, setLength] = useState<T.Length>("long");
function changeLength() {
setLength(o => (
o === "long"
? "short"
: o === "short" && "mini" in v.ps
? "mini"
: "long"
));
}
return <div className="text-center">
{"long" in v.ps && <div className="clickable small mb-1" onClick={changeLength}>{length}</div>}
<Border>
<>
{(v.type === "verb" || v.type === "perfectParticipleBlock") && v.complementWelded && <span className="mx-2">
<ComplementBlock opts={opts} comp={v.complementWelded.selection} script={script} inside />
</span>}
{getLength(v.ps, length)[0][script]}
</>
</Border>
<div>{v.type === "perfectParticipleBlock" ? "Past Partic." : "Verb"}</div>
<EnglishBelow>{(v.type === "perfectParticipleBlock"
? getEnglishParticipleInflection
: getEnglishPersonInfo
)(v.person, "short")}</EnglishBelow>
</div>
}
// function VerbSBlock({ opts, v, script }: {
// opts: T.TextOptions,
// script: "p" | "f",
// v: T.VerbRenderedBlock["block"] | T.PerfectParticipleBlock,
// }) {
// const [length, setLength] = useState<T.Length>("long");
// function changeLength() {
// setLength(o => (
// o === "long"
// ? "short"
// : o === "short" && "mini" in v.ps
// ? "mini"
// : "long"
// ));
// }
// return <div className="text-center">
// {"long" in v.ps && <div className="clickable small mb-1" onClick={changeLength}>{length}</div>}
// <Border>
// <>
// {(v.type === "verb" || v.type === "perfectParticipleBlock") && v.complementWelded && <span className="mx-2">
// <ComplementBlock opts={opts} comp={v.complementWelded.selection} script={script} inside />
// </span>}
// {getLength(v.ps, length)[0][script]}
// </>
// </Border>
// <div>{v.type === "perfectParticipleBlock" ? "Past Partic." : "Verb"}</div>
// <EnglishBelow>{(v.type === "perfectParticipleBlock"
// ? getEnglishParticipleInflection
// : getEnglishPersonInfo
// )(v.person, "short")}</EnglishBelow>
// </div>
// }
function ModalVerbBlock({ opts, v, script }: {
opts: T.TextOptions,
script: "p" | "f",
v: T.ModalVerbBlock,
}) {
const [length, setLength] = useState<T.Length>("long");
function changeLength() {
setLength(o => (
o === "long"
? "short"
: "long"
));
}
return <div className="text-center">
{"long" in v.ps && <div className="clickable small mb-1" onClick={changeLength}>{length}</div>}
<Border>
<>
{v.complementWelded && <span className="mx-2">
<ComplementBlock opts={opts} comp={v.complementWelded.selection} script={script} inside />
</span>}
{getLength(v.ps, length)[0][script]}
</>
</Border>
<div>Verb</div>
<EnglishBelow>Ability</EnglishBelow>
</div>
}
// function ModalVerbBlock({ opts, v, script }: {
// opts: T.TextOptions,
// script: "p" | "f",
// v: T.ModalVerbBlock,
// }) {
// const [length, setLength] = useState<T.Length>("long");
// function changeLength() {
// setLength(o => (
// o === "long"
// ? "short"
// : "long"
// ));
// }
// return <div className="text-center">
// {"long" in v.ps && <div className="clickable small mb-1" onClick={changeLength}>{length}</div>}
// <Border>
// <>
// {v.complementWelded && <span className="mx-2">
// <ComplementBlock opts={opts} comp={v.complementWelded.selection} script={script} inside />
// </span>}
// {getLength(v.ps, length)[0][script]}
// </>
// </Border>
// <div>Verb</div>
// <EnglishBelow>Ability</EnglishBelow>
// </div>
// }
function PerfHeadBlock({ opts, ps, script }: {
opts: T.TextOptions,
ps: T.PsString,
script: "p" | "f",
// function PerfHeadBlock({ opts, ps, script }: {
// opts: T.TextOptions,
// ps: T.PsString,
// script: "p" | "f",
}) {
return <div className="text-center">
<Border>
{ps[script]}
</Border>
<div>perf. head</div>
<EnglishBelow>{'\u00A0'}</EnglishBelow>
</div>;
}
// }) {
// return <div className="text-center">
// <Border>
// {ps[script]}
// </Border>
// <div>perf. head</div>
// <EnglishBelow>{'\u00A0'}</EnglishBelow>
// </div>;
// }
function ModalAuxBlock({ opts, aux, script }: {
opts: T.TextOptions,
aux: T.ModalVerbKedulPart,
script: "p" | "f",
// function ModalAuxBlock({ opts, aux, script }: {
// opts: T.TextOptions,
// aux: T.ModalVerbKedulPart,
// script: "p" | "f",
}) {
return <div className="text-center">
<Border>
{aux.ps[0][script]}
</Border>
<div>Abil. Aux</div>
<EnglishBelow>{getEnglishPersonInfo(aux.verb.block.person, "short")}</EnglishBelow>
</div>;
}
// }) {
// return <div className="text-center">
// <Border>
// {aux.ps[0][script]}
// </Border>
// <div>Abil. Aux</div>
// <EnglishBelow>{getEnglishPersonInfo(aux.verb.block.person, "short")}</EnglishBelow>
// </div>;
// }
function NegBlock({ opts, imperative, script }: {
opts: T.TextOptions,
@ -187,7 +188,7 @@ function NegBlock({ opts, imperative, script }: {
function EquativeBlock({ opts, eq, script }: {
opts: T.TextOptions,
eq: T.EquativeRendered | T.PerfectEquativeBlock,
eq: T.EquativeRendered,
script: "p" | "f",
}) {
const [length, setLength] = useState<T.Length>("long");

View File

@ -25,7 +25,6 @@ import {
getObjectSelectionFromBlocks,
getSubjectSelectionFromBlocks,
getSubjectSelection,
getVerbAndHeadFromBlocks,
} from "../../../lib/src/phrase-building/blocks-utils";
const correctEmoji = ["✅", '🤓', "✅", '😊', "🌹", "✅", "✅", '🥳', "👏", "✅", "💯", "😎", "✅", "👍"];
@ -354,17 +353,18 @@ function tickQuizState(startingWith: T.VPSelectionComplete | QuizState): QuizSta
}
function getBlanksAnswer(vps: T.VPSelectionComplete): { ps: T.PsString[], withBa: boolean } {
const { verb, perfectiveHead } = getVerbAndHeadFromBlocks(renderVP(vps).blocks);
const ps = flattenLengths(verb.block.ps).map(x => {
const y = removeBa(x);
if (perfectiveHead) {
return concatPsString(perfectiveHead.ps, y);
}
return y;
});
// TODO: !!!
// const { verb, perfectiveHead } = getVerbAndHeadFromBlocks(renderVP(vps).blocks);
// const ps = flattenLengths(verb.block.ps).map(x => {
// const y = removeBa(x);
// if (perfectiveHead) {
// return concatPsString(perfectiveHead.ps, y);
// }
// return y;
// });
return {
ps,
withBa: verb.block.hasBa,
ps: [{ p: "TOOD", f: "TODO" }],
withBa: false, // verb.block.hasBa,
}
}

View File

@ -12,7 +12,6 @@ import {
randomNumber,
} from "../lib/src/misc-helpers";
import { entryFeeder } from "./entryFeeder";
import { renderVerb } from "../lib/src/new-verb-engine/render-verb";
import NPPronounPicker from "../components/src/np-picker/NPPronounPicker";
const transitivities: T.Transitivity[] = [
@ -143,14 +142,6 @@ function VPBuilderDemo({ opts }: {
const makeVerbLabel = (entry: T.DictionaryEntry): string => (
`${entry.p} - ${clamp(entry.e, 20)}`
);
const rv = v ? renderVerb({
// verb: { entry: {"ts":1527815399,"i":15035,"p":"وهل","f":"wahul","g":"wahul","e":"to hit","r":4,"c":"v. trans.","tppp":"واهه","tppf":"waahu","ec":"hit,hits,hitting,hit,hit"} as T.VerbDictionaryEntry},
// verb: { entry: {"ts":1527814596,"i":8648,"p":"شرمول","f":"shărmawul","g":"sharmawul","e":"to shame, to disgrace, to dishonor, to embarrass","r":4,"c":"v. trans.","ec":"embarrass"} as T.VerbDictionaryEntry },
verb: v.verb as T.VerbEntry,
tense: testTense,
person: testPerson.person,
voice: testVoice,
}) : undefined;
// const rs = v ? getAllRs(v.verb as T.VerbEntry) : undefined
return <div className="mt-4">
<div className="d-block mx-auto card" style={{ maxWidth: "700px", background: "var(--closer)"}}>
@ -250,9 +241,6 @@ function VPBuilderDemo({ opts }: {
role="subject"
opts={opts}
/>
<pre>
{JSON.stringify(rv, null, " ")}
</pre>
{v?.verb.entry && <div style={{ paddingBottom: "20px" }}>
<PhraseBuilder
handleLinkClick="none"

View File

@ -8,6 +8,7 @@
import * as T from "../../types";
import { makePsString, removeFVarients } from "./accent-and-ps-utils";
import { applyToSingOrLengthOpts } from "./misc-helpers";
/**
* Returns a Pashto string (or string with Length options) ensuring that
@ -174,3 +175,60 @@ export function hasAccents(s: string | T.PsString): boolean {
if (typeof s !== "string") return hasAccents(s.f);
return accentReplacer.some((x) => s.includes(x.accented));
}
export function removeVerbAccent([a, b]: T.VerbRenderedOutput): T.VerbRenderedOutput {
return [
removeVHeadAccent(a),
removeVAccent(b),
];
}
function removeVHeadAccent([v]: [T.VHead] | []): [T.VHead] | [] {
if (v === undefined) {
return [];
}
if (v.type === "PH") {
return [{
...v,
ps: removeAccents(v.ps),
}];
}
return [{
...v,
comp: removeCompAccent(v.comp),
}];
}
function removeCompAccent(comp: T.Comp): T.Comp {
return {
...comp,
ps: removeAccents(comp.ps),
}
}
function removeVAccent(v: [T.VB, T.VBE] | [T.VBE]): [T.VB, T.VBE]| [T.VBE] {
return v.map(removeVBAccent) as [T.VB, T.VBE]| [T.VBE];
}
function removeVBAccent<V extends T.VB | T.VBE>(v: V): V {
if (v.type === "welded") {
return {
...v,
left: removeWeldedLeftAccent(v.left),
}
}
return {
...v,
ps: applyToSingOrLengthOpts(removeAccents, v.ps),
};
}
function removeWeldedLeftAccent(v: T.NComp | T.VBBasic | T.Welded) {
if (v.type === "NComp") {
return {
...v,
comp: removeCompAccent(v.comp),
};
}
return removeVBAccent(v);
}

View File

@ -17,7 +17,7 @@ export const dynamicAuxVerbs: Array<
}
> = [
{
entry: {"i":10058,"ts":1527812752,g: "","p":"کول","f":"kawul","e":"to do (an action or activity)","c":"v. trans. irreg. dyn. aux.","ssp":"وکړ","ssf":"óokR","prp":"وکړل","prf":"óokRul","pprtp":"کړی","pprtf":"kúRey","diacExcept":true},
entry: {"ts":1527812752,"i":11033,"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","separationAtP":1,"separationAtF":2,"diacExcept":true,"ec":"do,does,doing,did,done"},
},
{
entry: {"i":10122,"ts":1527812754,g: "","p":"کېدل","f":"kedul","e":"to happen, occur","c":"v. intrans. irreg. aux. dyn.","ssp":"وش","ssf":"óosh","prp":"وشول","prf":"óoshwul","pprtp":"شوی","pprtf":"shúwey","diacExcept":true},
@ -47,17 +47,7 @@ export const dynamicAuxVerbs: Array<
complement: {"i":3774,"ts":1527813139,g: "","p":"تېر","f":"ter","e":"last, past, previous, passed, gone over","c":"adj."},
},
{
entry: {
ts: 1527815399,
p: "وهل",
f: "wahul",
g: "",
e: "to hit",
c: "v. trans.",
i: 12183,
tppp: "واهه",
tppf: "waahu",
},
entry: {"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"},
},
{
entry: {

View File

@ -8,6 +8,20 @@
import * as T from "../../types";
export function applyToSingOrLengthOpts<X extends object>(f: (x: X) => X, x: T.SingleOrLengthOpts<X>): T.SingleOrLengthOpts<X> {
if ("long" in x) {
return {
long: f(x.long),
short: f(x.short),
..."mini" in x && x.mini ? {
mini: f(x.mini),
} : {},
};
} else {
return f(x);
}
}
export const blank: T.PsString = {
p: "_____",
f: "_____",

View File

@ -0,0 +1,233 @@
import * as T from "../../../types";
import { makeVerbSelection } from "./make-verb-selection";
import { vEntry } from "./rs-helpers";
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 kawulDyn = vEntry({"ts":1527812752,"i":11033,"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","separationAtP":1,"separationAtF":2});
const wahulNoC = vEntry({"ts":1527815399,"i":15049,"p":"وهل","f":"wahul","g":"wahul","e":"to hit","r":4,"tppp":"واهه","tppf":"waahu","ec":"hit,hits,hitting,hit,hit"});
const wahulNoC2 = vEntry({"ts":1527815399,"i":15049,"p":"وهل","f":"wahul","g":"wahul",c:"v","e":"to hit","r":4,"tppp":"واهه","tppf":"waahu","ec":"hit,hits,hitting,hit,hit"});
const khandul = vEntry({"ts":1527812767,"i":5896,"p":"خندل","f":"khandul","g":"khandul","e":"to laugh","r":4,"c":"v. gramm. trans.","psp":"خاند","psf":"khaand","ec":"laugh"});
const leedul = vEntry({"ts":1527812275,"i":12053,"p":"لیدل","f":"leedul","g":"leedul","e":"to see","r":4,"c":"v. trans./gramm. trans.","psp":"وین","psf":"ween","tppp":"لید","tppf":"leed","ec":"see,sees,seeing,saw,seen"});
const ghadzedul = vEntry({"ts":1527812615,"i":9500,"p":"غځېدل","f":"ghadzedul","g":"ghadzedul","e":"stretch out, lie, be extended, expand","r":3,"c":"v. intrans.","ec":"stretch","ep":"out"});
const bandawul = vEntry(
{"ts":1527821309,"i":1792,"p":"بندول","f":"bandawul","g":"bandawul","e":"to close, block, stop, barricade, cut off, restrain, hold back","r":3,"c":"v. stat. comp. trans.","l":1577301753727,"ec":"close"},
{"ts":1577301753727,"i":1780,"p":"بند","f":"band","g":"band","e":"closed, blocked, stopped","c":"adj."},
);
const bandedul = vEntry(
{"ts":1588781671306,"i":1796,"p":"بندېدل","f":"bandedúl","g":"bandedul","e":"to be closed, blocked, stopped","r":4,"c":"v. stat. comp. intrans.","l":1577301753727,"ec":"be","ep":"closed"},
{"ts":1577301753727,"i":1780,"p":"بند","f":"band","g":"band","e":"closed, blocked, stopped","c":"adj."},
);
const mundaWahul = vEntry(
{"ts":1527812939,"i":13322,"p":"منډه وهل","f":"munDa wahul","g":"munDawahul","e":"to run","r":4,"c":"v. dyn. comp. trans.","l":1527815805,"ec":"run,runs,running,ran,run"},
{"ts":1527815805,"i":13321,"p":"منډه","f":"múnDa","g":"munDa","e":"run, running","r":4,"c":"n. f."},
);
const istreeKawul = vEntry(
{"ts":1658796089458,"i":519,"p":"استري کول","f":"istree kawul","g":"istreekawul","e":"to iron (clothes etc.)","r":4,"c":"v. dyn./stat. comp. trans.","l":1658795458148,"ec":"iron"},
{"ts":1658795458148,"i":518,"p":"استري","f":"istree","g":"istree","e":"iron (for ironing clothes)","r":4,"c":"n. f."},
);
const cheeghKawul = vEntry(
{"ts":1608137130992,"i":5190,"p":"چیغه کول","f":"chéegha kawul","g":"cheeghakawul","e":"to yell, scream, cry out","r":3,"c":"v. gen. stat./dyn. comp. trans.","l":1527813972},
{"ts":1527813972,"i":5189,"p":"چیغه","f":"chéegha","g":"cheegha","e":"yell, scream, cry","r":3,"c":"n. f."},
);
const kaarKawul = vEntry(
{"ts":1527812732,"i":10270,"p":"کار کول","f":"kaar kawul","g":"kaarkawul","e":"to work","r":4,"c":"v. dyn. comp. trans.","l":1527822084,"ec":"work"},
{"ts":1527822084,"i":10268,"p":"کار","f":"kaar","g":"kaar","e":"work, job, business, stuff to do","r":4,"c":"n. m."},
);
const tests: {
verb: T.VerbEntry,
result: T.NewVerbSelection,
}[] = [
{
verb: wahul,
result: {
type: "verb",
verb: wahul,
voice: "active",
canChangeVoice: true,
transitivity: "transitive",
compound: false,
canBeGenStat: false,
canChangeTransGenTrans: false,
variableRs: false,
canChangeStatDyn: false,
negative: false,
tense: "presentVerb",
},
},
{
verb: ghadzedul,
result: {
type: "verb",
verb: ghadzedul,
voice: "active",
canChangeVoice: false,
transitivity: "intransitive",
compound: false,
canBeGenStat: false,
canChangeTransGenTrans: false,
variableRs: false,
canChangeStatDyn: false,
negative: false,
tense: "presentVerb",
},
},
{
verb: khandul,
result: {
type: "verb",
verb: khandul,
voice: "active",
canChangeVoice: false,
transitivity: "grammatically transitive",
compound: false,
canBeGenStat: false,
canChangeTransGenTrans: false,
variableRs: false,
canChangeStatDyn: false,
negative: false,
tense: "presentVerb",
},
},
{
verb: leedul,
result: {
type: "verb",
verb: leedul,
voice: "active",
canChangeVoice: true,
transitivity: "transitive",
compound: false,
canBeGenStat: false,
canChangeTransGenTrans: true,
variableRs: false,
canChangeStatDyn: false,
negative: false,
tense: "presentVerb",
},
},
{
verb: bandawul,
result: {
type: "verb",
verb: bandawul,
voice: "active",
canChangeVoice: true,
transitivity: "transitive",
compound: "stative",
canBeGenStat: false,
canChangeTransGenTrans: false,
variableRs: false,
canChangeStatDyn: false,
negative: false,
tense: "presentVerb",
},
},
{
verb: bandedul,
result: {
type: "verb",
verb: bandedul,
voice: "active",
canChangeVoice: false,
transitivity: "intransitive",
compound: "stative",
canBeGenStat: false,
canChangeTransGenTrans: false,
variableRs: false,
canChangeStatDyn: false,
negative: false,
tense: "presentVerb",
},
},
{
verb: mundaWahul,
result: {
type: "verb",
verb: mundaWahul,
voice: "active",
canChangeVoice: true,
transitivity: "transitive",
compound: "dynamic",
canBeGenStat: false,
canChangeTransGenTrans: false,
variableRs: false,
canChangeStatDyn: false,
negative: false,
tense: "presentVerb",
dynAuxVerb: wahul,
},
},
{
verb: istreeKawul,
result: {
type: "verb",
verb: istreeKawul,
voice: "active",
canChangeVoice: true,
transitivity: "transitive",
compound: "stative",
canBeGenStat: false,
canChangeTransGenTrans: false,
variableRs: false,
canChangeStatDyn: true,
negative: false,
tense: "presentVerb",
dynAuxVerb: kawulDyn,
},
},
{
verb: cheeghKawul,
result: {
type: "verb",
verb: cheeghKawul,
voice: "active",
canChangeVoice: false,
transitivity: "transitive",
compound: "dynamic",
canBeGenStat: true,
canChangeTransGenTrans: false,
variableRs: false,
canChangeStatDyn: false,
negative: false,
tense: "presentVerb",
dynAuxVerb: kawulDyn,
},
},
{
verb: kaarKawul,
result: {
type: "verb",
verb: kaarKawul,
voice: "active",
canChangeVoice: false,
transitivity: "transitive",
compound: "dynamic",
canBeGenStat: false,
canChangeTransGenTrans: false,
variableRs: false,
canChangeStatDyn: false,
negative: false,
tense: "presentVerb",
dynAuxVerb: kawulDyn,
},
},
];
test("verb selection", () => {
tests.forEach(({ verb, result }) => {
expect(makeVerbSelection(verb))
.toEqual(result);
});
});
test("verb selection failures", () => {
expect(() => {
makeVerbSelection(wahulNoC);
}).toThrow();
expect(() => {
makeVerbSelection(wahulNoC2);
}).toThrow();
});

View File

@ -0,0 +1,107 @@
import * as T from "../../../types";
import { removeFVarientsFromVerb } from "../accent-and-ps-utils";
import { dynamicAuxVerbs } from "../dyn-comp-aux-verbs";
export function makeVerbSelection(verb: T.VerbEntry): T.NewVerbSelection {
const v = removeFVarientsFromVerb(verb);
const {
transitivity,
canChangeTransGenTrans,
} = getTransitivity(v);
const compInfo = getCompoundInfo(v);
const canChangeVoice = transitivity === "transitive" && (compInfo.dynAuxVerb?.entry.p !== "کول" || compInfo.canChangeStatDyn) && !compInfo.canBeGenStat;
return {
type: "verb",
verb: v,
transitivity,
canChangeTransGenTrans,
...compInfo,
tense: "presentVerb",
voice: "active",
canChangeVoice,
negative: false,
variableRs: false,
}
}
function getCompoundInfo(v: T.VerbEntryNoFVars): {
compound: T.NewVerbSelection["compound"],
canChangeStatDyn: boolean,
dynAuxVerb?: T.NewVerbSelection["dynAuxVerb"],
canBeGenStat: boolean,
} {
const c = v.entry.c;
if (c.includes(" stat. comp.")) {
return {
compound: "stative",
canChangeStatDyn: false,
canBeGenStat: false,
};
}
if (c.includes(" dyn. comp.")) {
return {
compound: "dynamic",
canChangeStatDyn: false,
dynAuxVerb: getDynamicAuxVerb(v),
canBeGenStat: false,
};
}
if (c.includes(" dyn./stat. comp.")) {
return {
compound: "stative",
canChangeStatDyn: true,
dynAuxVerb: getDynamicAuxVerb(v),
canBeGenStat: false,
};
}
if (c.includes(" gen. stat./dyn. comp.")) {
return {
compound: "dynamic",
canBeGenStat: true,
dynAuxVerb: getDynamicAuxVerb(v),
canChangeStatDyn: false,
};
}
return {
compound: false,
canChangeStatDyn: false,
canBeGenStat: false,
};
}
function getTransitivity(v: T.VerbEntryNoFVars): {
transitivity: T.Transitivity,
canChangeTransGenTrans: boolean,
} {
const c = v.entry.c || "";
const canChangeTransGenTrans = c.includes(" trans./gramm. trans.");
if (c.includes(" intrans.")) {
return {
canChangeTransGenTrans,
transitivity: "intransitive",
};
}
if (c.includes("gramm. trans") && !canChangeTransGenTrans) {
return {
canChangeTransGenTrans,
transitivity: "grammatically transitive",
};
}
if (c.includes(" trans.")) {
return {
canChangeTransGenTrans,
transitivity: "transitive",
};
}
throw new Error("invalid or missing transitivity info in verb c");
}
function getDynamicAuxVerb({ entry }: T.VerbEntryNoFVars): T.VerbEntryNoFVars {
const auxWord = entry.p.trim().split(" ").slice(-1)[0];
const auxWordResult: T.VerbEntry | undefined = dynamicAuxVerbs.find((a) => a.entry.p === auxWord) as T.VerbEntry;
/* istanbul ignore next */
if (!auxWordResult) {
throw new Error("unknown auxilary verb for dynamic compound");
}
return removeFVarientsFromVerb(auxWordResult);
}

View File

@ -1,6 +1,7 @@
import { renderVerb } from "./render-verb";
import { vEntry } from "./rs-helpers";
import * as T from "../../../types";
import { negate } from "rambda";
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 raawrul = vEntry({"ts":1527815214,"i":6954,"p":"راوړل","f":"raawRúl","g":"raawRul","e":"to bring, deliver (inanimate objects)","r":4,"c":"v. trans.","tppp":"راووړ","tppf":"raawoR","noOo":true,"separationAtP":2,"separationAtF":3,"ec":"bring,brings,bringing,brought,brought"});
@ -52,6 +53,7 @@ test("basic tenses", () => {
tense: "presentVerb",
person: T.Person.FirstSingMale,
voice: "active",
negative: false,
})).toEqual({
hasBa: false,
@ -67,6 +69,7 @@ test("basic tenses", () => {
tense: "subjunctiveVerb",
person: T.Person.SecondSingMale,
voice: "active",
negative: false,
})).toEqual({
hasBa: false,
vbs: [
@ -81,6 +84,7 @@ test("basic tenses", () => {
tense: "habitualPerfectivePast",
person: T.Person.ThirdSingFemale,
voice: "active",
negative: false,
})).toEqual({
hasBa: true,
vbs: [
@ -106,6 +110,7 @@ test("basic tenses with inflecting roots/stems", () => {
person: T.Person.FirstSingMale,
presObj: T.Person.ThirdSingFemale,
voice: "active",
negative: false,
})).toEqual({
hasBa: false,
vbs: [
@ -517,6 +522,7 @@ test("special endings", () => {
tense: x.tense,
person: T.Person.ThirdSingMale,
voice: "active",
negative: false,
})).toEqual({ hasBa: false, vbs: x.result });
});
@ -525,6 +531,7 @@ test("special endings", () => {
tense: "perfectivePast",
person: T.Person.FirstPlurMale,
voice: "active",
negative: false,
})).toEqual({
hasBa: false,
vbs: [
@ -546,6 +553,7 @@ test("special endings", () => {
tense: "imperfectivePast",
person: T.Person.FirstSingMale,
voice: "active",
negative: false,
})).toEqual({
hasBa: false,
vbs: [
@ -568,6 +576,7 @@ test("special endings", () => {
tense: "imperfectivePast",
person: T.Person.ThirdSingFemale,
voice: "active",
negative: false,
})).toEqual({
hasBa: false,
vbs: [
@ -591,6 +600,7 @@ test("special endings", () => {
tense: "imperfectivePast",
person: T.Person.ThirdPlurMale,
voice: "active",
negative: false,
})).toEqual({
hasBa: false,
vbs: [
@ -612,6 +622,7 @@ test("special endings", () => {
tense: "perfectivePast",
person: T.Person.ThirdPlurMale,
voice: "active",
negative: false,
})).toEqual({
hasBa: false,
vbs: [
@ -636,6 +647,7 @@ test("imperative tenses", () => {
tense: "imperfectiveImperative",
person: T.Person.SecondSingMale,
voice: "active",
negative: false,
})).toEqual({
hasBa: false,
vbs: [
@ -650,6 +662,7 @@ test("imperative tenses", () => {
tense: "perfectiveImperative",
person: T.Person.SecondSingFemale,
voice: "active",
negative: false,
})).toEqual({
hasBa: false,
vbs: [
@ -664,6 +677,7 @@ test("imperative tenses", () => {
tense: "imperfectiveImperative",
person: T.Person.SecondPlurMale,
voice: "active",
negative: false,
})).toEqual({
hasBa: false,
vbs: [
@ -678,6 +692,7 @@ test("imperative tenses", () => {
tense: "perfectiveImperative",
person: T.Person.SecondPlurFemale,
voice: "active",
negative: false,
})).toEqual({
hasBa: false,
vbs: [
@ -695,6 +710,7 @@ test("ability tenses", () => {
tense: "presentVerbModal",
person: T.Person.FirstSingMale,
voice: "active",
negative: false,
})).toEqual({
hasBa: false,
vbs: [
@ -729,6 +745,7 @@ test("basic tenses", () => {
tense: "presentVerb",
person: T.Person.FirstSingMale,
voice: "active",
negative: false,
})).toEqual({
hasBa: false,
@ -744,6 +761,7 @@ test("basic tenses", () => {
tense: "subjunctiveVerb",
person: T.Person.SecondSingMale,
voice: "active",
negative: false,
})).toEqual({
hasBa: false,
vbs: [
@ -758,6 +776,7 @@ test("basic tenses", () => {
tense: "habitualPerfectivePast",
person: T.Person.ThirdSingFemale,
voice: "active",
negative: false,
})).toEqual({
hasBa: true,
vbs: [
@ -782,6 +801,7 @@ test("perfect tenses", () => {
tense: "presentPerfect",
person: T.Person.FirstSingMale,
voice: "active",
negative: false,
})).toEqual({
hasBa: false,
vbs: [
@ -797,6 +817,7 @@ test("perfect tenses", () => {
tense: "subjunctivePerfect",
person: T.Person.FirstSingMale,
voice: "active",
negative: false,
})).toEqual({
hasBa: false,
vbs: [
@ -812,6 +833,7 @@ test("perfect tenses", () => {
tense: "habitualPerfect",
person: T.Person.FirstSingMale,
voice: "active",
negative: false,
})).toEqual({
hasBa: false,
vbs: [
@ -827,6 +849,7 @@ test("perfect tenses", () => {
tense: "habitualPerfect",
person: T.Person.ThirdPlurMale,
voice: "active",
negative: false,
})).toEqual({
hasBa: false,
vbs: [
@ -842,6 +865,7 @@ test("perfect tenses", () => {
tense: "futurePerfect",
person: T.Person.FirstSingMale,
voice: "active",
negative: false,
})).toEqual({
hasBa: true,
vbs: [
@ -857,6 +881,7 @@ test("perfect tenses", () => {
tense: "pastPerfect",
person: T.Person.SecondSingFemale,
voice: "active",
negative: false,
})).toEqual({
hasBa: false,
vbs: [
@ -879,6 +904,7 @@ test("perfect tenses", () => {
tense: "wouldBePerfect",
person: T.Person.SecondSingFemale,
voice: "active",
negative: false,
})).toEqual({
hasBa: true,
vbs: [
@ -901,6 +927,7 @@ test("perfect tenses", () => {
tense: "pastSubjunctivePerfect",
person: T.Person.SecondSingFemale,
voice: "active",
negative: false,
})).toEqual({
hasBa: false,
vbs: [
@ -920,6 +947,7 @@ test("perfect tenses", () => {
tense: "wouldHaveBeenPerfect",
person: T.Person.SecondSingFemale,
voice: "active",
negative: false,
})).toEqual({
hasBa: true,
vbs: [
@ -942,7 +970,8 @@ test("ending on complex verbs", () => {
tense: "presentVerbModal",
person: T.Person.SecondSingMale,
voice: "active",
presObj: T.Person.ThirdSingFemale
presObj: T.Person.ThirdSingFemale,
negative: false,
})).toEqual({
hasBa: false,
vbs: [
@ -986,7 +1015,8 @@ test("ending on complex verbs", () => {
tense: "presentVerb",
person: T.Person.SecondSingMale,
voice: "active",
presObj: T.Person.ThirdSingFemale
presObj: T.Person.ThirdSingFemale,
negative: false,
})).toEqual({
hasBa: false,
vbs: [

View File

@ -24,8 +24,9 @@ import { getAspect, isKedul, perfectTenseToEquative, verbEndingConcat } from "./
import { accentOnNFromEnd, accentPsSyllable, removeAccents } from "../accent-helpers";
// TODO: problem with laaR - no perfective split
export function renderVerb({ verb, tense, person, voice, presObj }: {
export function renderVerb({ verb, tense, person, voice, presObj, negative }: {
verb: T.VerbEntry,
negative: boolean,
tense: T.VerbTense | T.PerfectTense | T.AbilityTense | T.ImperativeTense, // TODO: make T.Tense
person: T.Person,
voice: T.Voice,
@ -45,7 +46,7 @@ export function renderVerb({ verb, tense, person, voice, presObj }: {
gender: personGender(rootPerson),
number: personNumber(rootPerson),
};
const aspect = getAspect(tense);
const aspect = getAspect(tense, negative);
const isImperative = isImperativeTense(tense);
const type = isAbilityTense(tense) ? "ability" : "basic";

View File

@ -5,7 +5,7 @@ import { concatPsString, isUnisexSet, psStringFromEntry, trimOffPs } from "../p-
import { inflectPattern1 } from "./new-inflectors";
import { getLength } from "../p-text-helpers";
import { equativeEndings } from "../grammar-units";
import { isAdjectiveEntry } from "../type-predicates";
import { isAdjectiveEntry, isImperativeTense } from "../type-predicates";
import { inflectWord } from "../pashto-inflector";
export function isStatComp(v: T.VerbEntry): boolean {
@ -273,7 +273,10 @@ export function getLongVB(vb: T.VBA): T.VBNoLenghts<T.VBA> {
};
}
export function getAspect(tense: T.VerbTense | T.AbilityTense | T.ImperativeTense): T.Aspect {
export function getAspect(tense: T.VerbTense | T.AbilityTense | T.ImperativeTense, negative: boolean): T.Aspect {
if (isImperativeTense(tense) && negative) {
return "imperfective";
}
const t = tense.replace("Modal", "");
const imperfectives: Parameters<typeof getAspect>[0][] = ["presentVerb", "imperfectiveFuture", "imperfectivePast", "habitualImperfectivePast", "imperfectiveImperative"];
if (imperfectives.includes(t as Parameters<typeof getAspect>[0])) {

View File

@ -1,34 +1,6 @@
import * as T from "../../../types";
import { getLength } from "../p-text-helpers";
export function isRenderedVerbB(block: T.Block["block"]): block is T.RenderedVerbB {
if (block.type === "modalVerbBlock") {
return true;
}
if (block.type === "modalVerbKedulPart") {
return true;
}
if (block.type === "perfectEquativeBlock") {
return true;
}
if (block.type === "perfectParticipleBlock") {
return true;
}
if (block.type === "perfectiveHead") {
return true;
}
if (block.type === "verb") {
return true;
}
return false;
}
export function getVerbBlocks(blocks: T.Block[][]): T.RenderedVerbB[] {
return blocks[0]
.filter(b => isRenderedVerbB(b.block))
.map(b => b.block) as T.RenderedVerbB[];
}
export function makeBlock(block: T.Block["block"], key?: number): T.Block {
return {
key: key === undefined ? Math.random() : key,
@ -53,15 +25,6 @@ export function getSubjectSelection(blocks: T.EPSBlock[] | T.EPSBlockComplete[]
return b.block;
}
export function findPerfectiveHead(blocks: T.Block[][]): T.PerfectiveHeadBlock | undefined {
const b = blocks[0].find(f => f.block.type === "perfectiveHead");
if (!b || b.block.type !== "perfectiveHead") {
return undefined;
}
return b.block;
}
export function getSubjectSelectionFromBlocks(blocks: T.Block[][]): T.Rendered<T.SubjectSelectionComplete> {
const b = blocks[0].find(f => f.block.type === "subjectSelection");
if (!b || b.block.type !== "subjectSelection") {
@ -78,49 +41,6 @@ export function getObjectSelectionFromBlocks(blocks: T.Block[][]): T.Rendered<T.
return b.block;
}
export function getVerbFromBlocks(blocks: T.Block[][]): T.VerbRenderedBlock {
const b = blocks[0].find(f => f.block.type === "verb");
const p = blocks[0].find(f => f.block.type === "perfectParticipleBlock");
const m = blocks[0].find(f => f.block.type === "modalVerbBlock");
const v = (b && b.block.type === "verb")
? b.block
: (p && p.block.type === "perfectParticipleBlock")
? p.block.verb
: (m && m.block.type === "modalVerbBlock")
? m.block.verb
: undefined;
if (!v) {
throw new Error("verbSelection not found in blocks");
}
return v;
}
export function getLengthyFromBlocks(blocks: T.Block[][]): T.VerbRenderedBlock | T.PerfectParticipleBlock | T.ModalVerbBlock | T.PerfectEquativeBlock | undefined {
return blocks[0].find(f => f.block.type === "verb"
||
f.block.type === "perfectParticipleBlock"
||
f.block.type === "modalVerbBlock"
||
f.block.type === "perfectEquativeBlock"
)?.block as T.VerbRenderedBlock | T.PerfectParticipleBlock | T.ModalVerbBlock | T.PerfectEquativeBlock | undefined;
}
export function getVerbAndHeadFromBlocks(blocks: T.Block[][]): { verb: T.VerbRenderedBlock, perfectiveHead: T.PerfectiveHeadBlock | undefined } {
const verb = getVerbFromBlocks(blocks);
const perfectiveHead = blocks[0].find(f => f.block.type === "perfectiveHead");
if (!perfectiveHead || perfectiveHead.block.type !== "perfectiveHead") {
return {
verb,
perfectiveHead: undefined,
};
}
return {
verb,
perfectiveHead: perfectiveHead.block,
};
}
export function includesShrunkenServant(kids?: T.Kid[]): boolean {
if (!kids) return false;
return kids.some(k => (
@ -140,19 +60,6 @@ export function getAPsFromBlocks(blocks: T.Block[][]): T.Rendered<T.APSelection>
return blocks[0].filter(b => b.block.type === "AP").map(b => b.block) as T.Rendered<T.APSelection>[];
}
export function getComplementFromBlocks(blocks: T.Block[][]): T.Rendered<T.ComplementSelection> | T.Rendered<T.UnselectedComplementSelection> | undefined {
const complement = blocks[0].find(b => b.block.type === "complement")?.block as T.Rendered<T.ComplementSelection> | T.Rendered<T.UnselectedComplementSelection> | undefined;
if (complement) {
return complement;
}
// maybe there's a complement in the verb block
const verb = getVerbFromBlocks(blocks);
if (verb?.block.complementWelded) {
return verb.block.complementWelded;
}
return undefined;
}
export function getObjectSelection(blocks: T.VPSBlockComplete[]): T.ObjectSelectionComplete;
export function getObjectSelection(blocks: T.VPSBlock[]): T.ObjectSelection;
export function getObjectSelection(blocks: T.VPSBlock[] | T.VPSBlockComplete[]): T.ObjectSelection | T.ObjectSelectionComplete {
@ -337,56 +244,26 @@ export function specifyEquativeLength(blocksWVars: T.Block[][], length: "long" |
return blocksWVars.map(specify);
}
export function specifyBlockLength(blocksWVars: T.Block[][], length: "long" | "short" | "mini"): T.Block[][] {
function specify(blocks: T.Block[]): T.Block[] {
return blocks.map((block): T.Block => {
if (block.block.type === "verb") {
const v: T.Block = {
...block,
block: {
...block.block,
block: {
...block.block.block,
ps: getLength(block.block.block.ps, length),
},
},
};
return v;
}
if (block.block.type === "perfectEquativeBlock") {
const v: T.Block = {
...block,
block: {
...block.block,
ps: getLength(block.block.ps, length),
},
};
return v;
}
if (block.block.type === "perfectParticipleBlock") {
const p: T.Block = {
...block,
block: {
...block.block,
ps: getLength(block.block.ps, length),
},
};
return p;
}
if (block.block.type === "modalVerbBlock") {
const m: T.Block = {
...block,
block: {
...block.block,
ps: getLength(block.block.ps, length),
},
};
return m;
}
return block;
});
export function isRenderedVerbB({ block }: T.Block): boolean {
if (block.type === "equative") {
return true;
}
return blocksWVars.map(specify);
if (block.type === "VB") {
return true;
}
if (block.type === "PH") {
return true;
}
if (block.type === "NComp") {
return true;
}
if (block.type === "welded") {
return true;
}
if (block.type === "complement") {
return true;
}
return false
}
export function hasEquativeWithLengths(blocks: T.Block[][]): boolean {
@ -396,18 +273,6 @@ export function hasEquativeWithLengths(blocks: T.Block[][]): boolean {
return "long" in equative.block.equative.ps;
}
export function hasVerbWithLengths(blocks: T.Block[][]): boolean {
// TODO: handle length options with perfect verb equative as well?
const verb = blocks[0].find(x => (x.block.type === "verb" || x.block.type === "perfectParticipleBlock" || x.block.type === "modalVerbBlock"));
if (!verb) throw new Error("verb not found in blocks");
if (verb.block.type !== "verb" && verb.block.type !== "perfectParticipleBlock" && verb.block.type !== "modalVerbBlock") throw new Error("error finding verb in blocks");
return (
(verb.block.type === "verb" && "long" in verb.block.block.ps)
|| (verb.block.type === "perfectParticipleBlock" && "long" in verb.block.ps)
|| (verb.block.type === "modalVerbBlock" && "long" in verb.block.ps)
);
}
function arrayMove<X>(ar: X[], old_index: number, new_index: number): X[] {
const arr = [...ar];
const new_i = (new_index >= arr.length)

View File

@ -11,18 +11,14 @@ import {
import { getEnglishFromRendered, getPashtoFromRendered } from "./np-tools";
import { completeEPSelection, renderEP } from "./render-ep";
import { completeVPSelection } from "./vp-tools";
import { isStativeHelper, renderVP } from "./render-vp";
import { renderVP } from "./render-vp";
import {
getAPsFromBlocks,
getComplementFromBlocks,
getLengthyFromBlocks,
getObjectSelectionFromBlocks,
getPredicateSelectionFromBlocks,
getSubjectSelectionFromBlocks,
getVerbFromBlocks,
hasEquativeWithLengths,
isRenderedVerbB,
specifyBlockLength,
specifyEquativeLength,
} from "./blocks-utils";
import {
@ -77,29 +73,16 @@ export function compileEP(EP: T.EPRendered, combineLengths?: boolean, blankOut?:
export function compileVP(VP: T.VPRendered, form: T.FormVersion): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] };
export function compileVP(VP: T.VPRendered, form: T.FormVersion, combineLengths: true, blankOut?: BlankoutOptions): { ps: T.PsString[], e?: string [] };
export function compileVP(VP: T.VPRendered, form: T.FormVersion, combineLengths?: true, blankOut?: BlankoutOptions): { ps: T.SingleOrLengthOpts<T.PsString[]>, e?: string [] } {
const verb = getVerbFromBlocks(VP.blocks).block;
// const verb = getVerbFromBlocks(VP.blocks).block;
const psResult = compileVPPs(VP.blocks, VP.kids, form, VP.king, blankOut);
return {
ps: combineLengths ? flattenLengths(psResult) : psResult,
// TODO: English doesn't quite work for dynamic compounds in passive voice
e: (verb.voice === "passive" && VP.isCompound === "dynamic") ? undefined : compileEnglishVP(VP),
e: /* (verb.voice === "passive" && VP.isCompound === "dynamic") ? undefined : */ compileEnglishVP(VP),
};
}
function compileVPPs(blocks: T.Block[][], kids: T.Kid[], form: T.FormVersion, king: "subject" | "object", blankOut?: BlankoutOptions): T.SingleOrLengthOpts<T.PsString[]> {
const lengthyBlock = getLengthyFromBlocks(blocks);
const potentialLengthy = lengthyBlock?.type === "verb"
? lengthyBlock.block.ps
: lengthyBlock?.ps;
if (potentialLengthy && "long" in potentialLengthy) {
return {
long: compileVPPs(specifyBlockLength(blocks, "long"), kids, form, king, blankOut) as T.PsString[],
short: compileVPPs(specifyBlockLength(blocks, "short"), kids, form, king, blankOut) as T.PsString[],
..."mini" in potentialLengthy ? {
mini: compileVPPs(specifyBlockLength(blocks, "mini"), kids, form, king, blankOut) as T.PsString[],
} : {},
};
}
function compileVPPs(blocks: T.Block[][], kids: T.Kid[], form: T.FormVersion, king: "subject" | "object", blankOut?: BlankoutOptions): T.PsString[] {
const subjectPerson = getSubjectSelectionFromBlocks(blocks)
.selection.selection.person;
const blocksWKids = putKidsInKidsSection(
@ -176,7 +159,7 @@ function combineIntoText(piecesWVars: (T.Block | T.Kid | T.PsString)[][], subjec
? [first]
: (
(blankOut?.equative && "block" in first && first.block.type === "equative") ||
(blankOut?.verb && "block" in first && isRenderedVerbB(first.block)) ||
(blankOut?.verb && "block" in first && isRenderedVerbB(first)) ||
(blankOut?.predicate && "block" in first && first.block.type === "predicateSelection")
)
? [blank]
@ -192,7 +175,7 @@ function combineIntoText(piecesWVars: (T.Block | T.Kid | T.PsString)[][], subjec
firstPs.map(fPs => concatPsString(
fPs,
// TODO: this spacing is a mess and not accurate
(!("p" in first) && "block" in first && first.block.type === "perfectiveHead" && !("p" in next) && (("block" in next && (next.block.type === "verb" || next.block.type === "negative" || next.block.type === "modalVerbBlock")) || ("kid" in next && next.kid.type === "mini-pronoun")))
(!("p" in first) && "block" in first && first.block.type === "PH" && !("p" in next) && (("block" in next && (isRenderedVerbB(next) || next.block.type === "negative")) || ("kid" in next && next.kid.type === "mini-pronoun")))
? ((("block" in next && next.block.type === "negative") || ("kid" in next && next.kid.type === "mini-pronoun")) ? { p: "", f: " " } : "")
: " ",
r,
@ -220,46 +203,20 @@ function getPsFromPiece(piece: T.Block | T.Kid, subjectPerson: T.Person): T.PsSt
if (piece.block.type === "AP") {
return getPashtoFromRendered(piece.block, subjectPerson);
}
if (piece.block.type === "perfectiveHead") {
if (piece.block.type === "PH") {
return [piece.block.ps];
}
if (piece.block.type === "VB") {
return flattenLengths(piece.block.ps);
}
if (piece.block.type === "objectSelection") {
if (typeof piece.block.selection !== "object") {
return [{ p: "", f: "" }];
}
return getPashtoFromRendered(piece.block.selection, subjectPerson);
}
if (piece.block.type === "verb") {
// getLong is just for type safety - we will have split up the length options earlier in compileVPPs
const verbPs = getLong(piece.block.block.ps);
if (piece.block.block.complementWelded) {
return combineComplementWVerbPs(piece.block.block.complementWelded, verbPs);
}
return verbPs;
}
if (piece.block.type === "perfectParticipleBlock") {
// getLong is just for type safety - we will have split up the length options earlier in compileVPPs
const verbPs = getLong(piece.block.ps);
if (piece.block.complementWelded) {
return combineComplementWVerbPs(piece.block.complementWelded, verbPs);
}
return verbPs;
}
if (piece.block.type === "perfectEquativeBlock") {
// just using the short one for now - it will only be short anyways
return getShort(piece.block.ps);
}
if (piece.block.type === "modalVerbBlock") {
// getLong is just for type safety - we will have split up the length options earlier in compileVPPs
const verbPs = getLong(piece.block.ps);
if (piece.block.complementWelded) {
return combineComplementWVerbPs(piece.block.complementWelded, verbPs);
}
return verbPs;
}
if (piece.block.type === "modalVerbKedulPart") {
// just using the short one for now - it will only be short anyways
return getShort(piece.block.ps);
if (piece.block.type === "NComp") {
return [piece.block.comp.ps];
}
if (piece.block.type === "complement") {
if (piece.block.selection.type === "sandwich") {
@ -268,6 +225,8 @@ function getPsFromPiece(piece: T.Block | T.Kid, subjectPerson: T.Person): T.PsSt
}
return piece.block.selection.ps;
}
// welded
return getPsFromWelded(piece.block);
}
if ("kid" in piece) {
if (piece.kid.type === "ba") {
@ -280,13 +239,17 @@ function getPsFromPiece(piece: T.Block | T.Kid, subjectPerson: T.Person): T.PsSt
throw new Error("unrecognized piece type");
}
function combineComplementWVerbPs(comp: T.Rendered<T.ComplementSelection | T.UnselectedComplementSelection>, v: T.PsString[]): T.PsString[] {
const compPs = comp.selection.type === "sandwich"
? getPashtoFromRendered({ type: "AP", selection: comp.selection }, false)
: comp.selection.ps;
return compPs.flatMap((c) => (
v.map((p) => concatPsString(c, " ", p))
));
function getPsFromWelded(v: T.Welded): T.PsString[] {
function getPsFromSide(v: T.VBBasic | T.Welded | T.NComp | T.VBGenNum): T.PsString[] {
if (v.type === "VB") {
return flattenLengths(v.ps);
}
if (v.type === "NComp") {
return [v.comp.ps];
}
return getPsFromWelded(v);
}
return [...getPsFromSide(v.left), ...getPsFromSide(v.right)];
}
function getEngAPs(blocks: T.Block[][]): string {
@ -298,15 +261,16 @@ function getEngAPs(blocks: T.Block[][]): string {
}
function getEngComplement(blocks: T.Block[][]): string | undefined {
const comp = getComplementFromBlocks(blocks);
if (!comp) return undefined;
if (comp.selection.type === "unselected") {
return "____";
}
if (comp.selection.type === "sandwich") {
return getEnglishFromRendered({ type: "AP", selection: comp.selection });
}
return comp.selection.e;
return "TODO";
// const comp = getComplementFromBlocks(blocks);
// if (!comp) return undefined;
// if (comp.selection.type === "unselected") {
// return "____";
// }
// if (comp.selection.type === "sandwich") {
// return getEnglishFromRendered({ type: "AP", selection: comp.selection });
// }
// return comp.selection.e;
}
function putKidsInKidsSection(blocksWVars: T.Block[][], kids: T.Kid[], enforceKidsSectionBlankout: boolean): (T.Block | T.Kid | T.PsString)[][] {
@ -328,7 +292,8 @@ function compileEnglishVP(VP: T.VPRendered): string[] | undefined {
.replace("$SUBJ", subject)
.replace("$OBJ", object || "")
// add the complement in English if it's an external complement from a helper verb (kawul/kedul)
+ ((complement && isStativeHelper(getVerbFromBlocks(VP.blocks).block.verb))
// TODO!
+ (complement /* && isStativeHelper(getVerbFromBlocks(VP.blocks).block.verb))*/
? ` ${complement}`
: "")
+ APs;

View File

@ -1,31 +0,0 @@
import * as T from "../../../types";
function renderVerb(vs: T.VerbSelectionComplete): {
verbBlocks: VerbBlocks,
hasBa: boolean,
} {
const base = chooseRootOrStem(vs.tense, vs.verb);
const b = concatPsString(base, grammarUnits.presentEndings[0][0][0]);
return {
verbBlocks: [{
type: "verb",
block: {
...vs,
hasBa: false,
ps: [],
person: 0,
complementWelded: undefined,
}
}],
hasBa: false,
}
}
function chooseRootOrStem(tense: T.VerbFormName, entry: T.VerbEntry): T.FullForm<T.PsString> {
const info = getVerbInfo(entry.entry, entry.complement)
if ("stative" in info || "transitive" in info) {
throw new Error("multiple verb types not supported yet");
}
return info.stem.imperfective
}

View File

@ -1,5 +1,6 @@
import * as T from "../../../types";
import {
applyToSingOrLengthOpts,
getVerbBlockPosFromPerson,
} from "../misc-helpers";
import { conjugateVerb } from "../verb-conjugation";
@ -10,7 +11,7 @@ import {
splitOffLeapfrogWordFull,
getShort,
} from "../p-text-helpers";
import { removeAccents, removeAccentsWLength } from "../accent-helpers";
import { removeAccents, removeAccentsWLength, removeVerbAccent } from "../accent-helpers";
import {
getPersonFromNP,
removeBa,
@ -27,10 +28,11 @@ import {
isPerfectTense,
isTlulVerb,
} from "../type-predicates";
import { renderVerb } from "../new-verb-engine/render-verb";
import { renderEnglishVPBase } from "./english-vp-rendering";
import { personGender } from "../misc-helpers";
import { renderNPSelection } from "./render-np";
import { findPerfectiveHead, getObjectSelection, getSubjectSelection, makeBlock, makeKid } from "./blocks-utils";
import { getObjectSelection, getSubjectSelection, makeBlock, makeKid } from "./blocks-utils";
import { renderAPSelection } from "./render-ap";
import { findPossesivesToShrink, orderKids, getMiniPronounPs } from "./render-common";
import { renderComplementSelection } from "./render-complement";
@ -62,18 +64,23 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
king,
complementPerson,
});
const { verbBlocks, hasBa } = renderVerbSelection(VP.verb, kingPerson, objectPerson === undefined ? subjectPerson : objectPerson, VP.externalComplement);
const b: T.VPRendered = {
const { vbs, hasBa } = renderVerb({
verb: VP.verb.verb,
tense: VP.verb.tense,
person: kingPerson,
voice: VP.verb.voice,
negative: VP.verb.negative,
});
const VBwNeg = insertNegative(vbs, VP.verb.negative, isImperativeTense(VP.verb.tense));
// just enter the negative in the verb blocks
return {
type: "VPRendered",
king,
servant,
isPast,
isTransitive,
isCompound: VP.verb.isCompound,
blocks: insertNegative([
...firstBlocks,
...verbBlocks.map(makeBlock),
], VP.verb.negative, isImperativeTense(VP.verb.tense)),
blocks: VBwNeg.map(VBvars => [...firstBlocks, ...VBvars]),
kids: getVPKids(hasBa, VP.blocks, VP.form, king),
englishBase: renderEnglishVPBase({
subjectPerson,
@ -83,7 +90,6 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
form: VP.form,
whatsAdjustable: whatsAdjustable(VP),
};
return b;
}
function getVPKids(hasBa: boolean, blocks: T.VPSBlockComplete[], form: T.FormVersion, king: "subject" | "object"): T.Kid[] {
@ -116,156 +122,66 @@ function removeAbbreviated(blocks: T.VPSBlockComplete[], form: T.FormVersion, ki
})
}
function insertNegative(blocks: T.Block[], negative: boolean, imperative: boolean): T.Block[][] {
// TODO: proper arrange verb with negative and variations
// ALSO removing the accent from nu
if (!negative) return [blocks];
const blocksA = removeVerbAccent(blocks);
const basic: T.Block[] = [
...blocksA.slice(0, blocks.length - 1),
makeBlock({
type: "negative",
imperative,
}),
...blocksA.slice(-1),
];
const nonStandardPerfectiveSplit = hasNonStandardPerfectiveSplit(blocks);
if (nonStandardPerfectiveSplit) {
return [
basic,
[
...blocksA.slice(0, blocks.length - 2),
makeBlock({ type: "negative", imperative }),
...blocksA.slice(-2, -1), // second last (perfective split)
...blocksA.slice(-1), // last (verb)
],
];
function insertNegative(blocks: T.VerbRenderedOutput, negative: boolean, imperative: boolean): T.Block[][] {
if (!negative) {
return [blocks.flat().map(makeBlock)];
};
if (hasLeapFroggable(blocks)) {
const perfectiveHead = findPerfectiveHead([blocks]);
const perfectiveHeadReg = perfectiveHead && ["و", "وا"].includes(perfectiveHead.ps.p);
if (perfectiveHead) {
const blocksA = removeVerbAccent(blocks).flat().map(makeBlock);
const neg = makeBlock({ type: "negative", imperative });
const nonStandPerfectiveSplit = hasNonStandardPerfectiveSplit(blocks);
if (blocks[1].length === 2) {
// swapped ending with negative for ability and perfect verb forms
if (nonStandPerfectiveSplit) {
return [
[
...blocksA.slice(0, blocks.length - 2),
makeBlock({ type: "negative", imperative }),
...blocksA.slice(-1), // last
...blocksA.slice(-2, -1), // second last
],
[
...blocksA.slice(0, blocks.length - 2),
makeBlock({ type: "negative", imperative }),
...blocksA.slice(-2),
],
...!perfectiveHeadReg ? [basic] : [],
];
insertFromEnd(swapEndingBlocks(blocksA), neg, 2),
insertFromEnd(swapEndingBlocks(blocksA, 2), neg, 3),
insertFromEnd(blocksA, neg, 1),
]
}
return [
[
...blocksA.slice(0, blocks.length - 2),
makeBlock({ type: "negative", imperative }),
...blocksA.slice(-1), // last
...blocksA.slice(-2, -1), // second last
],
basic,
insertFromEnd(swapEndingBlocks(blocksA), neg, 2),
insertFromEnd(blocksA, neg, 1),
];
}
return [basic];
if (nonStandPerfectiveSplit) {
return [
insertFromEnd(blocksA, neg, 1),
insertFromEnd(blocksA, neg, 2),
];
} else {
return [insertFromEnd(blocksA, neg, 1)];
}
}
function hasLeapFroggable(blocks: T.Block[]): boolean {
return blocks.some(b => b.block.type === "perfectEquativeBlock" || b.block.type === "modalVerbBlock");
function swapEndingBlocks<X>(arr: X[], n: number = 1): X[] {
return [
...arr.slice(0, arr.length - (n + 1)),
...arr.slice(-n),
...arr.slice(-(n + 1), -n),
];
}
function hasNonStandardPerfectiveSplit(blocks: T.Block[]): boolean {
const perfS = blocks.find(b => b.block.type === "perfectiveHead");
if (!perfS || perfS.block.type !== "perfectiveHead") {
function insertFromEnd<X>(arr: X[], x: X, n: number): X[] {
if (n === 0) {
return [...arr, x];
}
return [
...arr.slice(0, arr.length - n),
x,
...arr.slice(-n),
];
}
function hasNonStandardPerfectiveSplit([[ph]]: T.VerbRenderedOutput): boolean {
if (!ph) {
return false;
}
return !["و", "وا"].includes(perfS.block.ps.p);
}
function removeVerbAccent(blocks: T.Block[]): T.Block[] {
return blocks.map((block) => {
if (block.block.type === "perfectiveHead") {
return {
...block,
block: {
...block.block,
ps: removeAccents(block.block.ps),
},
};
}
if (block.block.type === "verb") {
return {
...block,
block: {
...block.block,
block: {
...block.block.block,
ps: removeAccentsWLength(block.block.block.ps),
// The accent should ALREADY BE REMOVED FROM THE WELDED COMPLEMENT - BUT JUST TO BE SURE
...block.block.block.complementWelded ? {
complementWelded: removeAccentFromWeldedComplement(block.block.block.complementWelded),
} : {},
},
},
};
}
return block;
});
}
function removeAccentFromWeldedComplement(complement: T.Rendered<T.ComplementSelection> | T.Rendered<T.UnselectedComplementSelection>): T.Rendered<T.ComplementSelection> | T.Rendered<T.UnselectedComplementSelection> {
if (
complement.selection.type === "adjective"
|| complement.selection.type === "loc. adv."
|| complement.selection.type === "noun"
) {
return {
...complement,
selection: {
...complement.selection,
ps: removeAccents(complement.selection.ps),
},
};
if (ph.type !== "PH") {
return false;
}
if (complement.selection.type === "sandwich") {
return {
...complement,
selection: {
...complement.selection,
inside: removeAccentsFromNP(complement.selection.inside),
},
};
}
if (complement.selection.type === "unselected") {
return complement;
}
throw new Error("unexpected complement type");
}
function removeAccentsFromNP(np: T.Rendered<T.NPSelection>): T.Rendered<T.NPSelection> {
if (np.selection.type === "noun" || np.selection.type === "participle") {
return {
...np,
selection: {
...np.selection,
ps: removeAccents(np.selection.ps),
possesor: np.selection.possesor ? {
...np.selection.possesor,
np: removeAccentsFromNP(np.selection.possesor.np),
} : undefined,
},
};
}
return {
...np,
selection: {
...np.selection,
ps: removeAccents(np.selection.ps),
},
};
return !["و", "وا"].includes(ph.ps.p);
}
function shrinkServant(np: T.NPSelection): T.MiniPronoun {
@ -353,328 +269,6 @@ function whatsAdjustable(VP: T.VPSelectionComplete): "both" | "king" | "servant"
: "king";
}
type VerbBlocks =
| [T.PerfectiveHeadBlock, T.VerbRenderedBlock] // verb w perfective split
| [T.VerbRenderedBlock] // verb w/out perfective split
| [T.PerfectParticipleBlock, T.PerfectEquativeBlock] // perfect verb
| [T.ModalVerbBlock, T.ModalVerbKedulPart] // modal verb
function renderVerbSelection(vs: T.VerbSelectionComplete, person: T.Person, complementPerson: T.Person | undefined, externalComplement: T.ComplementSelection | T.UnselectedComplementSelection | undefined): {
verbBlocks: VerbBlocks
hasBa: boolean,
} {
const v = vs.dynAuxVerb || vs.verb;
const conjugations = conjugateVerb(v.entry, v.complement);
// TODO: error handle this?
const conj = "grammaticallyTransitive" in conjugations
// if there's an option for grammatically transitive or transitive
// will default to transitive
? conjugations.transitive
: "stative" in conjugations
? conjugations.stative
: conjugations;
const { ps: { head, rest }, hasBa } = getPsVerbConjugation(conj, vs, person, complementPerson);
const perfective = isPerfective(vs.tense);
const stativeHelper = isStativeHelper(vs.verb);
const complement: T.ComplementSelection | T.UnselectedComplementSelection | undefined = externalComplement
? externalComplement
: vs.isCompound === "stative"
? createComplementRetroactively(vs.verb.complement, complementPerson)
: undefined;
const renderedComplement = complement
? renderComplementSelection(complement, complementPerson || T.Person.FirstSingMale)
: undefined;
const vrb: T.VerbRenderedBlock = {
type: "verb",
block: {
...vs,
ps: (!perfective && renderedComplement && vs.verb.entry.p.includes(" "))
? removeComplement(rest, renderedComplement)
: rest,
person,
hasBa,
complementWelded: ((!perfective && renderedComplement) && (
// it's a stative helper (kawul/kedul) with an external complement
(stativeHelper && externalComplement)
||
// it's a stative compound with a space
(vs.verb.entry.p.includes(" "))
))
? removeAccentFromWeldedComplement(renderedComplement)
: undefined,
},
};
const verbBlocks = [
...(head ? (
(!!(vs.isCompound === "stative" && renderedComplement)) ? [
renderedComplement,
] : [{
type: "perfectiveHead",
ps: head,
} as T.PerfectiveHeadBlock]
) : [] as [T.Rendered<T.ComplementSelection>] | [T.PerfectiveHeadBlock] | []),
...(externalComplement && perfective && stativeHelper)
? [renderComplementSelection(externalComplement, complementPerson || T.Person.FirstSingMale)]
: [],
...splitUpIfModal(vrb),
] as VerbBlocks;
const perfectStuff = isPerfectTense(vrb.block.tense) ? getPerfectStuff(vrb) : undefined;
return {
verbBlocks: perfectStuff ? perfectStuff : verbBlocks,
hasBa,
};
}
function removeComplement(ps: T.SingleOrLengthOpts<T.PsString[]>, complement: T.Rendered<T.ComplementSelection | T.UnselectedComplementSelection>): T.SingleOrLengthOpts<T.PsString[]> {
if ("long" in ps) {
return {
long: removeComplement(ps.long, complement) as T.PsString[],
short: removeComplement(ps.short, complement) as T.PsString[],
...ps.mini ? {
mini: removeComplement(ps.mini, complement) as T.PsString[],
} : {},
};
}
const c = (complement.selection.type === "adjective"
? complement.selection.ps
: complement.selection.type === "loc. adv."
? complement.selection.ps
: complement.selection.type === "sandwich"
? complement.selection.inside.selection.ps
: complement.selection.type === "noun"
? complement.selection.ps
: complement.selection.ps);
// TODO: this is brutal - we could avoid this mess by redoing the verb conjugation engine
// to produce individual RenderedVerb objects instead of these tables with the complements in a string etc
const removed = ps.map(p => (
c.reduce((acc, v) => {
return {
p: acc.p.replace(`${v.p} `, ""),
// without accent sensitivity in the matching
// because the complement may or may not have had the accent removed
f: acc.f.replace(`${v.f} `, "")
.replace(`${removeAccents(v.f)} `, ""),
}
}, p)
));
return removed;
}
function createComplementRetroactively(complement: T.DictionaryEntry | undefined, person: T.Person | undefined): T.ComplementSelection {
// This is a bit of a hack to work with the current verb conjugation system.
if (!complement) {
throw new Error("error creating complement from head - no complement in verb entry");
}
// very important to have this before isNounEntry! (ie لېونی - n. m. anim unisex / adj. needs to be recognized as an adjective first!)
if (isAdjectiveEntry(complement)) {
if (person === undefined) {
throw new Error("there needs to be a complement person for rendering an adjective complement");
}
return {
type: "complement",
selection: {
type: "adjective",
entry: complement,
sandwich: undefined,
},
};
}
if (isNounEntry(complement)) {
return {
type: "complement",
selection: makeNounSelection(complement, undefined, undefined),
};
}
if (isLocativeAdverbEntry(complement)) {
return {
type: "complement",
selection: {
type: "loc. adv.",
entry: complement,
},
};
}
throw new Error("unsupported complement type");
}
export function isStativeHelper(v: T.VerbEntry): boolean {
if (v.entry.p === "کول" && v.entry.e.includes("make")) return true;
if (v.entry.p === "کېدل" && v.entry.e.includes("become")) return true;
return false;
}
function splitUpIfModal(v: T.VerbRenderedBlock): [T.VerbRenderedBlock] | [T.ModalVerbBlock, T.ModalVerbKedulPart] {
if (!isAbilityTense(v.block.tense)) {
return [v];
}
const [vrb, k] = splitOffLeapfrogWordFull(v.block.ps);
return [
{
type: "modalVerbBlock",
ps: vrb,
verb: v,
complementWelded: v.block.complementWelded,
},
{
type: "modalVerbKedulPart",
// sadly just for type safety - the conjugator always just gives us the short form
ps: getShort(k),
verb: v,
},
];
}
function getPerfectStuff(vrb: T.VerbRenderedBlock): [T.PerfectParticipleBlock, T.PerfectEquativeBlock] {
const [p, eq] = splitOffLeapfrogWordFull(vrb.block.ps);
return [
{
type: "perfectParticipleBlock",
ps: p,
person: vrb.block.person,
verb: vrb,
complementWelded: vrb.block.complementWelded,
},
{
type: "perfectEquativeBlock",
// TODO: right now the conjugator just always spits out the short form of the equative - would be nice to have both
ps: getShort(eq),
person: vrb.block.person,
},
];
}
function getPsVerbConjugation(conj: T.VerbConjugation, vs: T.VerbSelectionComplete, person: T.Person, objectPerson: T.Person | undefined): {
ps: {
head: T.PsString | undefined,
rest: T.SingleOrLengthOpts<T.PsString[]>,
},
hasBa: boolean,
} {
// TODO: handle the imperative form here
const f = getTenseVerbForm(conj, vs.tense, vs.voice, "phrase-building", vs.negative);
const block = getMatrixBlock(f, objectPerson, person);
const perfective = (vs.tense === "perfectiveImperative" && vs.negative)
? false
: isPerfective(vs.tense);
const verbForm = getVerbFromBlock(block, person);
const hasBa = hasBaParticle(getLong(verbForm)[0]);
if (perfective) {
const past = isPastTense(vs.tense);
const splitInfo = conj.info[(past || isAbilityTense(vs.tense)) ? "root" : "stem"].perfectiveSplit;
if (!splitInfo || (isTlulVerb(vs.verb.entry) && isAbilityTense(vs.tense))) {
return { ps: { head: undefined, rest: removeBaFromForm(verbForm) }, hasBa };
}
// TODO: Either solve this in the inflector or here, it seems silly (or redundant)
// to have a length option in the perfective split stem??
const [splitHead] = getLong(getMatrixBlock(splitInfo, objectPerson, person));
const ps = getHeadAndRest(splitHead, verbForm);
return {
hasBa,
ps,
};
}
return { hasBa, ps: { head: undefined, rest: removeBaFromForm(verbForm) }};
}
function removeBaFromForm(f: T.SingleOrLengthOpts<T.PsString[]>): T.SingleOrLengthOpts<T.PsString[]> {
if ("long" in f) {
return {
long: removeBaFromForm(f.long) as T.PsString[],
short: removeBaFromForm(f.short) as T.PsString[],
...f.mini ? {
mini: removeBaFromForm(f.mini) as T.PsString[],
} : {},
};
}
return f.map(removeBa);
}
function getVerbFromBlock(block: T.SingleOrLengthOpts<T.VerbBlock | T.ImperativeBlock>, person: T.Person): T.SingleOrLengthOpts<T.PsString[]> {
function grabFromBlock(b: T.VerbBlock | T.ImperativeBlock, [row, col]: [ row: number, col: number ]): T.PsString[] {
if (isImperativeBlock(b)) {
return b[personGender(person) === "masc" ? 0 : 1][col];
}
return b[row][col];
}
const pos = getVerbBlockPosFromPerson(person);
if ("long" in block) {
return {
long: grabFromBlock(block.long, pos),
short: grabFromBlock(block.short, pos),
...block.mini ? {
mini: grabFromBlock(block.mini, pos),
} : {},
};
}
return grabFromBlock(block, pos);
}
function getHeadAndRest(head: T.PsString, rest: T.PsString[]): { head: T.PsString | undefined, rest: T.PsString[] };
function getHeadAndRest(head: T.PsString, rest: T.SingleOrLengthOpts<T.PsString[]>): { head: T.PsString | undefined, rest: T.SingleOrLengthOpts<T.PsString[]> };
function getHeadAndRest(head: T.PsString, rest: T.SingleOrLengthOpts<T.PsString[]>): { head: T.PsString | undefined, rest: T.SingleOrLengthOpts<T.PsString[]> } {
if ("long" in rest) {
return {
// whether or not to include the head (for irreg tlul) -- eww // TODO: make nicer?
head: removeBa(rest.long[0]).p.slice(0, head.p.length) === head.p
? head : undefined,
rest: {
long: getHeadAndRest(head, rest.long).rest,
short: getHeadAndRest(head, rest.short).rest,
...rest.mini ? {
mini: getHeadAndRest(head, rest.mini).rest,
} : {},
},
};
}
let headMismatch = false;
const restM = rest.map((psRaw) => {
const ps = removeBa(psRaw);
const pMatches = removeAccents(ps.p.slice(0, head.p.length)) === head.p
const fMatches = removeAccents(ps.f.slice(0, head.f.length)) === removeAccents(head.f);
if (!(pMatches && fMatches)) {
headMismatch = true;
return psRaw;
// throw new Error(`split head does not match - ${JSON.stringify(ps)} ${JSON.stringify(head)}`);
}
return {
p: ps.p.slice(head.p.length),
f: ps.f.slice(head.f.length),
}
});
return {
head: headMismatch ? undefined : head,
rest: restM,
}
}
function getMatrixBlock<U>(f: {
mascSing: T.SingleOrLengthOpts<U>;
mascPlur: T.SingleOrLengthOpts<U>;
femSing: T.SingleOrLengthOpts<U>;
femPlur: T.SingleOrLengthOpts<U>;
} | T.SingleOrLengthOpts<U>, objectPerson: T.Person | undefined, kingPerson: T.Person): T.SingleOrLengthOpts<U> {
// @ts-ignore
if (!("mascSing" in f)) {
return f;
}
function personToLabel(p: T.Person): "mascSing" | "mascPlur" | "femSing" | "femPlur" {
if (p === T.Person.FirstSingMale || p === T.Person.SecondSingMale || p === T.Person.ThirdSingMale) {
return "mascSing";
}
if (p === T.Person.FirstSingFemale || p === T.Person.SecondSingFemale || p === T.Person.ThirdSingFemale) {
return "femSing";
}
if (p === T.Person.FirstPlurMale || p === T.Person.SecondPlurMale || p === T.Person.ThirdPlurMale) {
return "mascPlur";
}
return "femPlur";
}
// if there's an object the matrix will agree with that, otherwise with the kingPerson (subject for intransitive)
const person = (objectPerson === undefined) ? kingPerson : objectPerson;
return f[personToLabel(person)];
}
export function getKingAndServant(isPast: boolean, isTransitive: boolean):
{ king: "subject", servant: "object" } |
{ king: "object", servant: "subject" } |
@ -697,23 +291,6 @@ function isFirstOrSecondPersPronoun(o: "none" | T.NPSelection | T.Person.ThirdPl
return [0,1,2,3,6,7,8,9].includes(o.selection.person);
}
function isPerfective(t: T.Tense): boolean {
if (isPerfectTense(t)) return false;
if (t === "presentVerb" || t === "imperfectiveFuture" || t === "imperfectivePast" || t === "habitualImperfectivePast") {
return false;
}
if (t === "perfectiveFuture" || t === "subjunctiveVerb" || t === "perfectivePast" || t === "habitualPerfectivePast") {
return true;
}
if (t === "perfectiveFutureModal" || t === "subjunctiveVerbModal" || t === "perfectivePastModal" || t === "habitualPerfectivePastModal") {
return true;
}
if (t === "perfectiveImperative") {
return true;
}
return false;
}
function isMascSingAnimatePattern4(np: T.NPSelection): boolean {
if (np.selection.type !== "noun") {
return false;

View File

@ -1298,7 +1298,7 @@ const toTest = [
entry: {"i":9369,"ts":1527822084,"p":"کار","g":"","f":"kaar","e":"work, job, business, stuff to do","c":"n. m."},
person: 4,
},
auxVerb: {"i":10058,"ts":1527812752,"p":"کول","g":"","f":"kawul","e":"to do (an action or activity)","c":"v. trans. irreg. dyn. aux.","ssp":"وکړ","ssf":"óokR","prp":"وکړل","prf":"óokRul","pprtp":"کړی","pprtf":"kúRey","diacExcept":true},
auxVerb: {"ts":1527812752,"i":11033,"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","separationAtP":1,"separationAtF":2,"diacExcept":true,"ec":"do,does,doing,did,done"},
intransitiveForm: {
entry: {
entry: {"i":9371,"ts":1527812732,"p":"کار کېدل","g":"","f":"kaar kedul","e":"to work","l":1527822084,"c":"v. intrans. dyn. comp."},
@ -1377,17 +1377,7 @@ const toTest = [
plural: { p: "منډې", f: "munDe" },
person: 11,
},
auxVerb: {
ts: 1527815399,
p: "وهل",
f: "wahul",
g:"",
e: "to hit",
c: "v. trans.",
i: 12183,
tppp: "واهه",
tppf: "waahu",
},
auxVerb: {"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"},
singularForm: {
entry: {
entry: {"i":12101,"ts":1527812939,"p":"منډې وهل","g":"","f":"munDe wahul","e":"to run","l":1527815805,"c":"v. dyn. comp. trans. sing. or plur."},
@ -1423,17 +1413,7 @@ const toTest = [
entry: {"i":12098,"ts":1527815805,"p":"منډه","g":"","f":"múnDa","e":"run, running","c":"n. f."},
person: 5,
},
auxVerb: {
ts: 1527815399,
p: "وهل",
f: "wahul",
g:"",
e: "to hit",
c: "v. trans.",
i: 12183,
tppp: "واهه",
tppf: "waahu",
},
auxVerb: {"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"},
},
},
},
@ -1537,7 +1517,7 @@ const toTest = [
entry: {"i":7909,"ts":1527819252,"p":"شروع","g":"","f":"shUróo'","e":"beginning, start, undertaking","c":"n. m."},
person: 4,
},
auxVerb: {"i":10058,"ts":1527812752,"p":"کول","g":"","f":"kawul","e":"to do (an action or activity)","c":"v. trans. irreg. dyn. aux.","ssp":"وکړ","ssf":"óokR","prp":"وکړل","prf":"óokRul","pprtp":"کړی","pprtf":"kúRey","diacExcept":true},
auxVerb: {"ts":1527812752,"i":11033,"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","separationAtP":1,"separationAtF":2,"diacExcept":true,"ec":"do,does,doing,did,done"},
intransitiveForm: {
entry: {
entry: {"i":7910,"ts":1527819253,"p":"شروع کېدل","g":"","f":"shUróo' kedul","e":"to start, to begin","l":1527819252,"c":"v. intrans. dyn. comp."},
@ -1680,7 +1660,7 @@ const toTest = [
entry: {"i":4769,"ts":1527813972,"p":"چیغه","g":"","f":"chéegha","e":"yell, scream, cry","c":"n. f."},
person: 5,
},
auxVerb: {"i":10058,"ts":1527812752,"p":"کول","g":"","f":"kawul","e":"to do (an action or activity)","c":"v. trans. irreg. dyn. aux.","ssp":"وکړ","ssf":"óokR","prp":"وکړل","prf":"óokRul","pprtp":"کړی","pprtf":"kúRey","diacExcept":true},
auxVerb: {"ts":1527812752,"i":11033,"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","separationAtP":1,"separationAtF":2,"diacExcept":true,"ec":"do,does,doing,did,done"},
intransitiveForm: {
entry: {
entry: {"i":4770,"ts":1608137130992,"p":"چیغه کېدل","g":"","f":"chéegha kedul","e":"to yell, scream, cry out","l":1527813972,"c":"v. intrans. dyn. comp."},
@ -1887,22 +1867,7 @@ const toTest = [
},
"person": 11
},
"auxVerb": {
"i": 10058,
"ts": 1527812752,
"p": "کول",
"f": "kawul",
"g": "",
"e": "to do (an action or activity)",
"c": "v. trans. irreg. dyn. aux.",
"ssp": "وکړ",
"ssf": "óokR",
"prp": "وکړل",
"prf": "óokRul",
"pprtp": "کړی",
"pprtf": "kúRey",
"diacExcept": true
},
"auxVerb": {"ts":1527812752,"i":11033,"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","separationAtP":1,"separationAtF":2,"diacExcept":true,"ec":"do,does,doing,did,done"},
"singularForm": {
entry: {
entry: {"i":4770,"ts":1608137130992,"p":"چیغې کول","g":"","f":"chéeghe kawul","e":"to yell, scream, cry out","l":1527813972,"c":"v. dyn. comp. trans."},
@ -2014,22 +1979,7 @@ const toTest = [
},
"person": 5
},
"auxVerb": {
"i": 10058,
"ts": 1527812752,
"p": "کول",
"f": "kawul",
"g": "",
"e": "to do (an action or activity)",
"c": "v. trans. irreg. dyn. aux.",
"ssp": "وکړ",
"ssf": "óokR",
"prp": "وکړل",
"prf": "óokRul",
"pprtp": "کړی",
"pprtf": "kúRey",
"diacExcept": true
},
"auxVerb": {"ts":1527812752,"i":11033,"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","separationAtP":1,"separationAtF":2,"diacExcept":true,"ec":"do,does,doing,did,done"},
"intransitiveForm": {
entry: {
entry: {"i":4770,"ts":1608137130992,"p":"چیغې کېدل","g":"","f":"chéeghe kedul","e":"to yell, scream, cry out","l":1527813972,"c":"v. intrans. dyn. comp."},

View File

@ -6,6 +6,10 @@
*
*/
type Prettify<T> = {
[K in keyof T]: T[K];
} & {};
export type PsStringField = "p" | "f";
export type PsString = {
[k in PsStringField]: string;
@ -558,7 +562,7 @@ export type UnisexAnimNounEntry = UnisexNounEntry & { __brand4: "an anim unisex
export type AdverbEntry = DictionaryEntry & { c: string } & { __brand: "an adverb entry" };
export type LocativeAdverbEntry = AdverbEntry & { __brand2: "a locative adverb entry" };
export type AdjectiveEntry = DictionaryEntry & { c: string } & { __brand: "an adjective entry" };
export type VerbDictionaryEntry = DictionaryEntry & { __brand: "a verb entry" };
export type VerbDictionaryEntry = DictionaryEntry & { c: string } & { __brand: "a verb entry" };
export type VerbEntry = {
entry: VerbDictionaryEntry,
// TODO: the compliment could also be typed? Maybe?
@ -668,6 +672,22 @@ export type VerbSelectionComplete = Omit<VerbSelection, "object" | "verbTense" |
export type Voice = "active" | "passive";
export type NewVerbSelection = {
type: "verb",
verb: VerbEntryNoFVars,
dynAuxVerb?: VerbEntryNoFVars,
voice: Voice,
canChangeVoice: boolean,
negative: boolean,
tense: VerbTense | PerfectTense | AbilityTense | ImperativeTense,
transitivity: Transitivity,
canChangeTransGenTrans: boolean,
compound: "stative" | "dynamic" | false,
canChangeStatDyn: boolean,
canBeGenStat: boolean,
variableRs: boolean,
}
export type VerbSelection = {
type: "verb",
verb: VerbEntry,
@ -1001,40 +1021,6 @@ export type EntryLookupPortal<X extends VerbEntry | DictionaryEntry> = {
export type EquativeBlock = { type: "equative", equative: EquativeRendered };
export type PerfectParticipleBlock = {
type: "perfectParticipleBlock",
ps: SingleOrLengthOpts<PsString[]>,
verb: VerbRenderedBlock,
person: Person,
complementWelded: undefined | Rendered<ComplementSelection> | Rendered<UnselectedComplementSelection>,
};
export type PerfectEquativeBlock = {
type: "perfectEquativeBlock",
ps: PsString[],
person: Person,
};
export type ModalVerbBlock = {
type: "modalVerbBlock",
ps: SingleOrLengthOpts<PsString[]>,
verb: VerbRenderedBlock,
complementWelded: undefined | Rendered<ComplementSelection> | Rendered<UnselectedComplementSelection>,
};
export type ModalVerbKedulPart = {
type: "modalVerbKedulPart",
ps: PsString[],
verb: VerbRenderedBlock,
};
export type PerfectiveHeadBlock = { type: "perfectiveHead", ps: PsString };
export type VerbRenderedBlock = {
type: "verb",
block: Omit<VerbSelectionComplete, "object"> & {
hasBa: boolean,
ps: SingleOrLengthOpts<PsString[]>,
person: Person,
complementWelded: undefined | Rendered<ComplementSelection> | Rendered<UnselectedComplementSelection>,
},
};
export type Block = {
key: number,
block: | Rendered<SubjectSelectionComplete>
@ -1043,22 +1029,12 @@ export type Block = {
| Rendered<PredicateSelectionComplete>
| Rendered<ComplementSelection>
| Rendered<UnselectedComplementSelection>
| PerfectParticipleBlock
| PerfectEquativeBlock
| ModalVerbBlock
| ModalVerbKedulPart
| { type: "negative", imperative: boolean }
| PerfectiveHeadBlock
| VerbRenderedBlock
| EquativeBlock;
}
export type RenderedVerbB = VerbRenderedBlock
| PerfectiveHeadBlock
| ModalVerbBlock
| ModalVerbKedulPart
| PerfectEquativeBlock
| PerfectParticipleBlock;
| EquativeBlock
| VB
| VBE
| VHead,
};
export type Kid = {
key: number,
@ -1124,7 +1100,7 @@ export type NComp = {
comp: Comp,
};
// Complement can be one of
// element can be one of
// - adjective
// - locative adv
// - sandwich (TODO)