more towards parsing vps
This commit is contained in:
parent
3221bb1e89
commit
4893ef0d70
|
@ -20,6 +20,7 @@
|
|||
"@types/react": "^17.0.3",
|
||||
"@types/react-dom": "^17.0.2",
|
||||
"bootstrap": "^4.6.0",
|
||||
"jest-extended": "^4.0.1",
|
||||
"node-fetch": "^3.2.10",
|
||||
"node-fetch-commonjs": "^3.2.4",
|
||||
"pbf": "^3.2.1",
|
||||
|
@ -76,4 +77,4 @@
|
|||
]
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
}
|
|
@ -33,4 +33,4 @@
|
|||
"@types/pbf": "^3.0.2",
|
||||
"rollup": "^2.79.1"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,10 +31,32 @@ export function lookup(s: Partial<T.DictionaryEntry>): T.DictionaryEntry[] {
|
|||
return nounsAdjs.filter((e) => e[key] === value) as T.DictionaryEntry[];
|
||||
}
|
||||
|
||||
export function verbLookup(
|
||||
s: (e: T.VerbDictionaryEntry) => boolean
|
||||
): T.VerbEntry[] {
|
||||
return verbs.filter(({ entry }) => s(entry));
|
||||
export function verbLookup(input: string): T.VerbEntry[] {
|
||||
const s = input.slice(0, -1);
|
||||
if (s.endsWith("ېږ")) {
|
||||
return verbs.filter(
|
||||
({ entry }) =>
|
||||
entry.p.slice(0, -1) === s ||
|
||||
entry.p === s.slice(0, -1) + "دل" ||
|
||||
entry.p === s ||
|
||||
entry.psp === s ||
|
||||
entry.prp === s ||
|
||||
entry.ssp === s
|
||||
);
|
||||
}
|
||||
return verbs.filter(
|
||||
({ entry }) =>
|
||||
entry.p.slice(0, -1) === s ||
|
||||
// for short intransitive forms
|
||||
entry.p.slice(0, -3) === s ||
|
||||
entry.p === s ||
|
||||
entry.psp === s ||
|
||||
entry.prp === s ||
|
||||
entry.ssp === s ||
|
||||
(entry.separationAtP &&
|
||||
(entry.p.slice(entry.separationAtP) === s ||
|
||||
entry.psp?.slice(entry.separationAtP) === s))
|
||||
);
|
||||
}
|
||||
|
||||
export function wordQuery(word: string, type: "adj"): T.AdjectiveEntry;
|
||||
|
|
|
@ -0,0 +1,413 @@
|
|||
/* eslint-disable jest/valid-title */
|
||||
import * as T from "../../../types";
|
||||
import { verbLookup, wordQuery } from "./lookup";
|
||||
import { parseVerb } from "./parse-verb";
|
||||
import { tokenizer } from "./tokenizer";
|
||||
|
||||
const wahul = wordQuery("وهل", "verb");
|
||||
const leekul = wordQuery("لیکل", "verb");
|
||||
const manul = wordQuery("منل", "verb");
|
||||
const rasedul = wordQuery("رسېدل", "verb");
|
||||
const leedul = wordQuery("لیدل", "verb");
|
||||
const khorul = wordQuery("خوړل", "verb");
|
||||
const kenaastul = wordQuery("کېناستل", "verb");
|
||||
const prexodul = wordQuery("پرېښودل", "verb");
|
||||
const xodul = wordQuery("ښودل", "verb");
|
||||
const kexodul = wordQuery("کېښودل", "verb");
|
||||
|
||||
const tests: {
|
||||
label: string;
|
||||
cases: {
|
||||
input: string;
|
||||
output: {
|
||||
root?: {
|
||||
persons: T.Person[];
|
||||
aspects: T.Aspect[];
|
||||
};
|
||||
stem?: {
|
||||
persons: T.Person[];
|
||||
aspects: T.Aspect[];
|
||||
};
|
||||
verb: T.VerbEntry;
|
||||
}[];
|
||||
}[];
|
||||
}[] = [
|
||||
{
|
||||
label: "with regular simple verbs",
|
||||
cases: [
|
||||
{
|
||||
input: "وهلم",
|
||||
output: [
|
||||
{
|
||||
root: {
|
||||
persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: wahul,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
input: "وهم",
|
||||
output: [
|
||||
{
|
||||
root: {
|
||||
persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
stem: {
|
||||
persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: wahul,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
input: "وهې",
|
||||
output: [
|
||||
{
|
||||
root: {
|
||||
persons: [
|
||||
T.Person.SecondSingMale,
|
||||
T.Person.SecondSingFemale,
|
||||
T.Person.ThirdPlurFemale,
|
||||
],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
stem: {
|
||||
persons: [T.Person.SecondSingMale, T.Person.SecondSingFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: wahul,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
input: "لیکم",
|
||||
output: [
|
||||
{
|
||||
root: {
|
||||
persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
stem: {
|
||||
persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: leekul,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
input: "لیکلو",
|
||||
output: [
|
||||
{
|
||||
root: {
|
||||
persons: [T.Person.FirstPlurMale, T.Person.FirstPlurFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: leekul,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
input: "لیکل",
|
||||
output: [
|
||||
{
|
||||
root: {
|
||||
persons: [T.Person.ThirdPlurMale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: leekul,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
input: "لیکلل",
|
||||
output: [],
|
||||
},
|
||||
{
|
||||
input: "منله",
|
||||
output: [
|
||||
{
|
||||
root: {
|
||||
persons: [T.Person.ThirdSingFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: manul,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
input: "مني",
|
||||
output: [
|
||||
{
|
||||
stem: {
|
||||
persons: [
|
||||
T.Person.ThirdSingMale,
|
||||
T.Person.ThirdSingFemale,
|
||||
T.Person.ThirdPlurMale,
|
||||
T.Person.ThirdPlurFemale,
|
||||
],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: manul,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
input: "منئ",
|
||||
output: [
|
||||
{
|
||||
stem: {
|
||||
persons: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
root: {
|
||||
persons: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: manul,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "with regular intransitive verbs",
|
||||
cases: [
|
||||
{
|
||||
input: "رسېدلم",
|
||||
output: [
|
||||
{
|
||||
root: {
|
||||
persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: rasedul,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
input: "رسېدم",
|
||||
output: [
|
||||
{
|
||||
root: {
|
||||
persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: rasedul,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
input: "رسېږې",
|
||||
output: [
|
||||
{
|
||||
stem: {
|
||||
persons: [T.Person.SecondSingMale, T.Person.SecondSingFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: rasedul,
|
||||
},
|
||||
],
|
||||
},
|
||||
// short version of intransitive as well
|
||||
{
|
||||
input: "رسئ",
|
||||
output: [
|
||||
{
|
||||
stem: {
|
||||
persons: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: rasedul,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "verbs with irregular stems",
|
||||
cases: [
|
||||
{
|
||||
input: "وینم",
|
||||
output: [
|
||||
{
|
||||
stem: {
|
||||
persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: leedul,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
input: "وینم",
|
||||
output: [
|
||||
{
|
||||
stem: {
|
||||
persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: leedul,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
input: "لیده",
|
||||
output: [
|
||||
{
|
||||
root: {
|
||||
persons: [T.Person.ThirdSingFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: leedul,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
input: "خورې",
|
||||
output: [
|
||||
{
|
||||
stem: {
|
||||
persons: [T.Person.SecondSingMale, T.Person.SecondSingFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: khorul,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
input: "خوړي",
|
||||
output: [],
|
||||
},
|
||||
{
|
||||
input: "خوړم",
|
||||
output: [
|
||||
{
|
||||
root: {
|
||||
persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: khorul,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "verbs with seperating prefix",
|
||||
cases: [
|
||||
{
|
||||
input: "کېني",
|
||||
output: [
|
||||
{
|
||||
stem: {
|
||||
persons: [
|
||||
T.Person.ThirdSingMale,
|
||||
T.Person.ThirdSingFemale,
|
||||
T.Person.ThirdPlurMale,
|
||||
T.Person.ThirdPlurFemale,
|
||||
],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: kenaastul,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
input: "نم",
|
||||
output: [
|
||||
{
|
||||
stem: {
|
||||
persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale],
|
||||
aspects: ["perfective"],
|
||||
},
|
||||
verb: kenaastul,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
input: "پرېږدو",
|
||||
output: [
|
||||
{
|
||||
stem: {
|
||||
persons: [T.Person.FirstPlurMale, T.Person.FirstPlurFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: prexodul,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
input: "پرېښوده",
|
||||
output: [
|
||||
{
|
||||
root: {
|
||||
persons: [T.Person.ThirdSingFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: prexodul,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
input: "ښودله",
|
||||
output: [
|
||||
{
|
||||
root: {
|
||||
persons: [T.Person.ThirdSingFemale],
|
||||
aspects: ["imperfective", "perfective"],
|
||||
},
|
||||
verb: xodul,
|
||||
},
|
||||
{
|
||||
root: {
|
||||
persons: [T.Person.ThirdSingFemale],
|
||||
aspects: ["perfective"],
|
||||
},
|
||||
verb: prexodul,
|
||||
},
|
||||
{
|
||||
root: {
|
||||
persons: [T.Person.ThirdSingFemale],
|
||||
aspects: ["perfective"],
|
||||
},
|
||||
verb: kexodul,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
tests.forEach(({ label, cases }) => {
|
||||
test(label, () => {
|
||||
cases.forEach(({ input, output }) => {
|
||||
const tokens = tokenizer(input);
|
||||
const vbs = parseVerb(tokens, verbLookup).map((r) => r.body);
|
||||
const madeVbsS = output.reduce<Omit<T.VBE, "ps">[]>((acc, o) => {
|
||||
return [
|
||||
...acc,
|
||||
...(["root", "stem"] as const).flatMap((base) =>
|
||||
(o[base]?.aspects || []).flatMap((aspect) =>
|
||||
(o[base]?.persons || []).flatMap((person) => ({
|
||||
type: "VB" as const,
|
||||
person,
|
||||
info: {
|
||||
type: "verb" as const,
|
||||
aspect,
|
||||
base,
|
||||
verb: o.verb,
|
||||
},
|
||||
}))
|
||||
)
|
||||
),
|
||||
];
|
||||
}, []);
|
||||
expect(vbs).toIncludeSameMembers(madeVbsS);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -2,63 +2,183 @@ import * as T from "../../../types";
|
|||
|
||||
export function parseVerb(
|
||||
tokens: Readonly<T.Token[]>,
|
||||
verbLookup: (s: (e: T.VerbDictionaryEntry) => boolean) => T.VerbEntry[]
|
||||
verbLookup: (s: string) => T.VerbEntry[]
|
||||
): T.ParseResult<Omit<T.VBE, "ps">>[] {
|
||||
if (tokens.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const [first, ...rest] = tokens;
|
||||
const people = getVerbEnding(first.s);
|
||||
if (people.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const verbs = findByStem(first.s.slice(0, -1), verbLookup);
|
||||
|
||||
return people.flatMap((person) =>
|
||||
verbs.map((verb) => ({
|
||||
tokens: rest,
|
||||
body: {
|
||||
type: "VB",
|
||||
person,
|
||||
info: {
|
||||
type: "verb",
|
||||
aspect: "imperfective",
|
||||
base: "stem",
|
||||
verb,
|
||||
},
|
||||
},
|
||||
errors: [],
|
||||
}))
|
||||
);
|
||||
// TODO: can optimize this to not have to look for possible stems/roots if none
|
||||
const verbs = verbLookup(first.s);
|
||||
return matchVerbs(first.s, verbs, people).map((body) => ({
|
||||
tokens: rest,
|
||||
body,
|
||||
errors: [],
|
||||
}));
|
||||
}
|
||||
|
||||
function getVerbEnding(p: string): T.Person[] {
|
||||
function matchVerbs(
|
||||
s: string,
|
||||
entries: T.VerbEntry[],
|
||||
people: {
|
||||
root: T.Person[];
|
||||
stem: T.Person[];
|
||||
}
|
||||
): Omit<T.VBE, "ps">[] {
|
||||
const w: Omit<T.VBE, "ps">[] = [];
|
||||
const base = s.endsWith("ل") ? s : s.slice(0, -1);
|
||||
if (people.stem.length) {
|
||||
const stemMatches = {
|
||||
imperfective: entries.filter(({ entry: e }) => {
|
||||
if (e.c.includes("comp")) {
|
||||
return false;
|
||||
}
|
||||
if (e.psp) {
|
||||
return e.psp === base;
|
||||
}
|
||||
if (e.c.includes("intrans.")) {
|
||||
const miniRoot = e.p.slice(0, -3);
|
||||
return miniRoot + "ېږ" === base || miniRoot === base;
|
||||
} else {
|
||||
return e.p.slice(0, -1) === base;
|
||||
}
|
||||
}),
|
||||
perfective: entries.filter(({ entry: e }) => {
|
||||
if (e.c.includes("comp")) {
|
||||
return false;
|
||||
}
|
||||
if (e.ssp) {
|
||||
const bSep = e.separationAtP ? e.ssp.slice(e.separationAtP) : "";
|
||||
return bSep === base || e.ssp === base;
|
||||
}
|
||||
if (e.psp) {
|
||||
const bSep = e.separationAtP ? e.psp.slice(e.separationAtP) : "";
|
||||
return bSep === base || e.psp === base;
|
||||
}
|
||||
if (e.c.includes("intrans.")) {
|
||||
const miniRoot = e.p.slice(0, -3);
|
||||
return miniRoot + "ېږ" === base || miniRoot === base;
|
||||
} else {
|
||||
return e.p.slice(0, -1) === base;
|
||||
}
|
||||
}),
|
||||
};
|
||||
Object.entries(stemMatches).forEach(([aspect, entries]) => {
|
||||
entries.forEach((verb) => {
|
||||
people.stem.forEach((person) => {
|
||||
w.push({
|
||||
type: "VB",
|
||||
person,
|
||||
info: {
|
||||
type: "verb",
|
||||
aspect: aspect as T.Aspect,
|
||||
base: "stem",
|
||||
verb: verb,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
if (people.root.length) {
|
||||
const rootMatches = {
|
||||
imperfective: entries.filter(
|
||||
({ entry: e }) =>
|
||||
!e.c.includes("comp") &&
|
||||
(base === e.p || (!s.endsWith("ل") && base === e.p.slice(0, -1)))
|
||||
),
|
||||
perfective: entries.filter(({ entry: e }) => {
|
||||
if (e.c.includes("comp")) {
|
||||
return false;
|
||||
}
|
||||
if (e.separationAtP) {
|
||||
const bSep = e.p.slice(e.separationAtP);
|
||||
return (
|
||||
base === bSep ||
|
||||
base === e.p ||
|
||||
(!s.endsWith("ل") &&
|
||||
(base === e.p.slice(0, -1) || base === bSep.slice(0, -1)))
|
||||
);
|
||||
} else {
|
||||
// TODO: perfective roots are so rare could optimize this with a couple of checks?
|
||||
return e.prp
|
||||
? e.prp === base || e.prp.slice(0, -1) === base
|
||||
: base === e.p || (!s.endsWith("ل") && base === e.p.slice(0, -1));
|
||||
}
|
||||
}),
|
||||
};
|
||||
Object.entries(rootMatches).forEach(([aspect, entries]) => {
|
||||
entries.forEach((verb) => {
|
||||
people.root.forEach((person) => {
|
||||
w.push({
|
||||
type: "VB",
|
||||
person,
|
||||
info: {
|
||||
type: "verb",
|
||||
aspect: aspect as T.Aspect,
|
||||
base: "root",
|
||||
verb: verb,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
function getVerbEnding(p: string): {
|
||||
root: T.Person[];
|
||||
stem: T.Person[];
|
||||
} {
|
||||
if (p.endsWith("م")) {
|
||||
return [T.Person.FirstSingMale, T.Person.FirstSingFemale];
|
||||
return {
|
||||
root: [T.Person.FirstSingMale, T.Person.FirstSingFemale],
|
||||
stem: [T.Person.FirstSingMale, T.Person.FirstSingFemale],
|
||||
};
|
||||
} else if (p.endsWith("ې")) {
|
||||
return [T.Person.SecondSingMale, T.Person.SecondSingFemale];
|
||||
return {
|
||||
root: [
|
||||
T.Person.SecondSingMale,
|
||||
T.Person.SecondSingFemale,
|
||||
T.Person.ThirdPlurFemale,
|
||||
],
|
||||
stem: [T.Person.SecondSingMale, T.Person.SecondSingFemale],
|
||||
};
|
||||
} else if (p.endsWith("ي")) {
|
||||
return [
|
||||
T.Person.ThirdSingMale,
|
||||
T.Person.ThirdSingFemale,
|
||||
T.Person.ThirdPlurMale,
|
||||
T.Person.ThirdPlurFemale,
|
||||
];
|
||||
return {
|
||||
stem: [
|
||||
T.Person.ThirdSingMale,
|
||||
T.Person.ThirdSingFemale,
|
||||
T.Person.ThirdPlurMale,
|
||||
T.Person.ThirdPlurFemale,
|
||||
],
|
||||
root: [],
|
||||
};
|
||||
} else if (p.endsWith("و")) {
|
||||
return [T.Person.FirstPlurMale, T.Person.FirstPlurFemale];
|
||||
return {
|
||||
root: [T.Person.FirstPlurMale, T.Person.FirstPlurFemale],
|
||||
stem: [T.Person.FirstPlurMale, T.Person.FirstPlurFemale],
|
||||
};
|
||||
} else if (p.endsWith("ئ")) {
|
||||
return [T.Person.SecondPlurMale, T.Person.SecondPlurFemale];
|
||||
return {
|
||||
root: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale],
|
||||
stem: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale],
|
||||
};
|
||||
} else if (p.endsWith("ه")) {
|
||||
return {
|
||||
root: [T.Person.ThirdSingFemale],
|
||||
stem: [],
|
||||
};
|
||||
} else if (p.endsWith("ل")) {
|
||||
return {
|
||||
root: [T.Person.ThirdPlurMale],
|
||||
stem: [],
|
||||
};
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function findByStem(
|
||||
stem: string,
|
||||
verbLookup: (s: (e: T.VerbDictionaryEntry) => boolean) => T.VerbEntry[]
|
||||
): T.VerbEntry[] {
|
||||
return verbLookup(
|
||||
(e) =>
|
||||
e.psp === stem ||
|
||||
(!e.psp && !e.c.includes("comp") && e.p.slice(0, -1) === stem)
|
||||
);
|
||||
return {
|
||||
root: [],
|
||||
stem: [],
|
||||
};
|
||||
}
|
||||
|
|
|
@ -28,42 +28,167 @@ const kedulStat = vEntry({
|
|||
ec: "become",
|
||||
});
|
||||
|
||||
// cool examples: زه خوږې ماشومې وهم
|
||||
|
||||
export function parseVP(
|
||||
tokens: Readonly<T.Token[]>,
|
||||
lookup: (s: Partial<T.DictionaryEntry>) => T.DictionaryEntry[],
|
||||
verbLookup: (s: (e: T.VerbDictionaryEntry) => boolean) => T.VerbEntry[]
|
||||
verbLookup: (s: string) => T.VerbEntry[]
|
||||
): T.ParseResult<T.VPSelectionComplete>[] {
|
||||
if (tokens.length === 0) {
|
||||
return [];
|
||||
}
|
||||
// how to make this into a nice pipeline... 🤔
|
||||
const NP1 = parseNP(tokens, lookup).filter(({ errors }) => !errors.length);
|
||||
const NP2 = bindParseResult(
|
||||
NP1,
|
||||
(tokens) => parseNP(tokens, lookup),
|
||||
true
|
||||
).filter(({ errors }) => !errors.length);
|
||||
const vb = bindParseResult(
|
||||
NP2,
|
||||
(tokens) => parseVerb(tokens, verbLookup),
|
||||
true
|
||||
).filter(({ errors }) => !errors.length);
|
||||
const NP2 = bindParseResult<
|
||||
{
|
||||
inflected: boolean;
|
||||
selection: T.NPSelection;
|
||||
},
|
||||
{
|
||||
np1: {
|
||||
inflected: boolean;
|
||||
selection: T.NPSelection;
|
||||
};
|
||||
np2:
|
||||
| {
|
||||
inflected: boolean;
|
||||
selection: T.NPSelection;
|
||||
}
|
||||
| undefined;
|
||||
}
|
||||
>(NP1, (tokens, np1) => {
|
||||
const np2s = parseNP(tokens, lookup);
|
||||
if (!np2s.length) {
|
||||
const r: T.ParseResult<{
|
||||
np1: {
|
||||
inflected: boolean;
|
||||
selection: T.NPSelection;
|
||||
};
|
||||
np2: undefined;
|
||||
}>[] = [
|
||||
{
|
||||
tokens,
|
||||
body: {
|
||||
np1,
|
||||
np2: undefined,
|
||||
},
|
||||
errors: [],
|
||||
},
|
||||
];
|
||||
return r;
|
||||
}
|
||||
return np2s.map((p) => ({
|
||||
tokens: p.tokens,
|
||||
body: {
|
||||
np1,
|
||||
np2: p.body,
|
||||
},
|
||||
errors: p.errors,
|
||||
}));
|
||||
}).filter(({ errors }) => !errors.length);
|
||||
const vb = bindParseResult(NP2, (tokens, nps) => {
|
||||
const vb = parseVerb(tokens, verbLookup);
|
||||
// TODO make a nice functor that just maps or adds in the body
|
||||
return vb.map((p) => ({
|
||||
tokens: p.tokens,
|
||||
body: {
|
||||
np2: nps.np2,
|
||||
v: p.body,
|
||||
np1: nps.np1,
|
||||
},
|
||||
errors: p.errors,
|
||||
}));
|
||||
}).filter(({ errors }) => !errors.length);
|
||||
// TODO: be able to bind mulitple vals
|
||||
return bindParseResult<Omit<T.VBE, "ps">, T.VPSelectionComplete>(
|
||||
vb,
|
||||
(tokens, v) => {
|
||||
const w: T.ParseResult<T.VPSelectionComplete>[] = [];
|
||||
NP1.forEach(({ body: np1 }) => {
|
||||
NP2.forEach(({ body: np2 }) => {
|
||||
// NICE TODO: IF there's an error in any of the NPS, don't try
|
||||
// to make the VPS - just show them that error
|
||||
// for that we probably need a different type of
|
||||
[
|
||||
[np1, np2],
|
||||
[np2, np1],
|
||||
].forEach(([s, o]) => {
|
||||
const errors: T.ParseError[] = [];
|
||||
const subjPerson = getPersonFromNP(s.selection);
|
||||
return bindParseResult(vb, (tokens, { np1, np2, v }) => {
|
||||
const w: T.ParseResult<T.VPSelectionComplete>[] = [];
|
||||
const isPast = v.info.type === "verb" && v.info.base === "root";
|
||||
const intransitive =
|
||||
v.info.type === "verb" && v.info.verb.entry.c.includes("intrans.");
|
||||
|
||||
if (!np2) {
|
||||
const errors: T.ParseError[] = [];
|
||||
const s = np1;
|
||||
if (!intransitive) {
|
||||
return [];
|
||||
}
|
||||
if (s.inflected) {
|
||||
errors.push({
|
||||
message: "subject of intransitive verb should not be inflected",
|
||||
});
|
||||
}
|
||||
if (getPersonFromNP(s.selection) !== v.person) {
|
||||
errors.push({
|
||||
message: "subject should agree with intransitive verb",
|
||||
});
|
||||
}
|
||||
const blocks: T.VPSBlockComplete[] = [
|
||||
{
|
||||
key: 1,
|
||||
block: makeSubjectSelectionComplete(s.selection),
|
||||
},
|
||||
{
|
||||
key: 2,
|
||||
block: {
|
||||
type: "objectSelection",
|
||||
selection: "none",
|
||||
},
|
||||
},
|
||||
];
|
||||
const verb: T.VerbSelectionComplete = {
|
||||
type: "verb",
|
||||
verb: v.info.type === "verb" ? v.info.verb : kedulStat,
|
||||
transitivity: "intransitive",
|
||||
canChangeTransitivity: false,
|
||||
canChangeStatDyn: false,
|
||||
negative: false,
|
||||
tense: isPast ? "imperfectivePast" : "presentVerb",
|
||||
canChangeVoice: true,
|
||||
isCompound: false,
|
||||
voice: "active",
|
||||
};
|
||||
w.push({
|
||||
tokens,
|
||||
body: {
|
||||
blocks,
|
||||
verb,
|
||||
externalComplement: undefined,
|
||||
form: {
|
||||
removeKing: false,
|
||||
shrinkServant: false,
|
||||
},
|
||||
},
|
||||
errors,
|
||||
});
|
||||
} else {
|
||||
[[np1, np2, false] as const, [np2, np1, true] as const].forEach(
|
||||
([s, o, reversed]) => {
|
||||
const subjPerson = getPersonFromNP(s.selection);
|
||||
const errors: T.ParseError[] = [];
|
||||
if (intransitive) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (isPast) {
|
||||
if (getPersonFromNP(o.selection) !== v.person) {
|
||||
errors.push({
|
||||
message: "transitive past tense verb does not match object",
|
||||
});
|
||||
} else {
|
||||
if (!s.inflected) {
|
||||
errors.push({
|
||||
message: "transitive past tense subject should be inflected",
|
||||
});
|
||||
}
|
||||
if (o.inflected) {
|
||||
errors.push({
|
||||
message:
|
||||
"transitive past tense object should not be inflected",
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (getPersonFromNP(s.selection) !== v.person) {
|
||||
errors.push({
|
||||
message: "verb does not match subject",
|
||||
|
@ -83,46 +208,49 @@ export function parseVP(
|
|||
errors.push({ message: "object should not be inflected" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const blocks: T.VPSBlockComplete[] = [
|
||||
{
|
||||
key: 1,
|
||||
block: makeSubjectSelectionComplete(s.selection),
|
||||
const blocks: T.VPSBlockComplete[] = [
|
||||
{
|
||||
key: 1,
|
||||
block: makeSubjectSelectionComplete(s.selection),
|
||||
},
|
||||
{
|
||||
key: 2,
|
||||
block: makeObjectSelectionComplete(o.selection),
|
||||
},
|
||||
];
|
||||
if (reversed) {
|
||||
blocks.reverse();
|
||||
}
|
||||
const verb: T.VerbSelectionComplete = {
|
||||
type: "verb",
|
||||
verb: v.info.type === "verb" ? v.info.verb : kedulStat,
|
||||
transitivity: "transitive",
|
||||
canChangeTransitivity: false,
|
||||
canChangeStatDyn: false,
|
||||
negative: false,
|
||||
tense: isPast ? "imperfectivePast" : "presentVerb",
|
||||
canChangeVoice: true,
|
||||
isCompound: false,
|
||||
voice: "active",
|
||||
};
|
||||
w.push({
|
||||
tokens,
|
||||
body: {
|
||||
blocks,
|
||||
verb,
|
||||
externalComplement: undefined,
|
||||
form: {
|
||||
removeKing: false,
|
||||
shrinkServant: false,
|
||||
},
|
||||
{
|
||||
key: 2,
|
||||
block: makeObjectSelectionComplete(o.selection),
|
||||
},
|
||||
];
|
||||
const verb: T.VerbSelectionComplete = {
|
||||
type: "verb",
|
||||
verb: v.info.type === "verb" ? v.info.verb : kedulStat,
|
||||
transitivity: "transitive",
|
||||
canChangeTransitivity: false,
|
||||
canChangeStatDyn: false,
|
||||
negative: false,
|
||||
tense: "presentVerb",
|
||||
canChangeVoice: true,
|
||||
isCompound: false,
|
||||
voice: "active",
|
||||
};
|
||||
w.push({
|
||||
tokens,
|
||||
body: {
|
||||
blocks,
|
||||
verb,
|
||||
externalComplement: undefined,
|
||||
form: {
|
||||
removeKing: false,
|
||||
shrinkServant: false,
|
||||
},
|
||||
},
|
||||
errors,
|
||||
});
|
||||
},
|
||||
errors,
|
||||
});
|
||||
});
|
||||
});
|
||||
return w;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
return w;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,4 +2,8 @@
|
|||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
||||
import "@testing-library/jest-dom";
|
||||
|
||||
// add all jest-extended matchers
|
||||
import * as matchers from "jest-extended";
|
||||
expect.extend(matchers);
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
"src",
|
||||
"testSetup.js"
|
||||
]
|
||||
}
|
||||
|
|
54
yarn.lock
54
yarn.lock
|
@ -1433,6 +1433,13 @@
|
|||
optionalDependencies:
|
||||
node-notifier "^8.0.0"
|
||||
|
||||
"@jest/schemas@^29.6.0":
|
||||
version "29.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.0.tgz#0f4cb2c8e3dca80c135507ba5635a4fd755b0040"
|
||||
integrity sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==
|
||||
dependencies:
|
||||
"@sinclair/typebox" "^0.27.8"
|
||||
|
||||
"@jest/source-map@^26.6.2":
|
||||
version "26.6.2"
|
||||
resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz"
|
||||
|
@ -1640,6 +1647,11 @@
|
|||
estree-walker "^1.0.1"
|
||||
picomatch "^2.2.2"
|
||||
|
||||
"@sinclair/typebox@^0.27.8":
|
||||
version "0.27.8"
|
||||
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
|
||||
integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==
|
||||
|
||||
"@sinonjs/commons@^1.7.0":
|
||||
version "1.8.3"
|
||||
resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz"
|
||||
|
@ -4283,6 +4295,11 @@ diff-sequences@^27.0.6:
|
|||
resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.0.6.tgz"
|
||||
integrity sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ==
|
||||
|
||||
diff-sequences@^29.4.3:
|
||||
version "29.4.3"
|
||||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2"
|
||||
integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==
|
||||
|
||||
diffie-hellman@^5.0.0:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz"
|
||||
|
@ -6555,6 +6572,16 @@ jest-diff@^27.0.0:
|
|||
jest-get-type "^27.0.6"
|
||||
pretty-format "^27.2.4"
|
||||
|
||||
jest-diff@^29.0.0:
|
||||
version "29.6.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.6.2.tgz#c36001e5543e82a0805051d3ceac32e6825c1c46"
|
||||
integrity sha512-t+ST7CB9GX5F2xKwhwCf0TAR17uNDiaPTZnVymP9lw0lssa9vG+AFyDZoeIHStU3WowFFwT+ky+er0WVl2yGhA==
|
||||
dependencies:
|
||||
chalk "^4.0.0"
|
||||
diff-sequences "^29.4.3"
|
||||
jest-get-type "^29.4.3"
|
||||
pretty-format "^29.6.2"
|
||||
|
||||
jest-docblock@^26.0.0:
|
||||
version "26.0.0"
|
||||
resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz"
|
||||
|
@ -6598,6 +6625,14 @@ jest-environment-node@^26.6.2:
|
|||
jest-mock "^26.6.2"
|
||||
jest-util "^26.6.2"
|
||||
|
||||
jest-extended@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/jest-extended/-/jest-extended-4.0.1.tgz#2315cb5914fc132e5acd07945bfaa01aac3947c2"
|
||||
integrity sha512-KM6dwuBUAgy6QONuR19CGubZB9Hkjqvl/d5Yc/FXsdB8+gsGxB2VQ+NEdOrr95J4GMPeLnDoPOKyi6+mKCCnZQ==
|
||||
dependencies:
|
||||
jest-diff "^29.0.0"
|
||||
jest-get-type "^29.0.0"
|
||||
|
||||
jest-get-type@^26.3.0:
|
||||
version "26.3.0"
|
||||
resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz"
|
||||
|
@ -6608,6 +6643,11 @@ jest-get-type@^27.0.6:
|
|||
resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.0.6.tgz"
|
||||
integrity sha512-XTkK5exIeUbbveehcSR8w0bhH+c0yloW/Wpl+9vZrjzztCPWrxhHwkIFpZzCt71oRBsgxmuUfxEqOYoZI2macg==
|
||||
|
||||
jest-get-type@^29.0.0, jest-get-type@^29.4.3:
|
||||
version "29.4.3"
|
||||
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5"
|
||||
integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==
|
||||
|
||||
jest-haste-map@^26.6.2:
|
||||
version "26.6.2"
|
||||
resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz"
|
||||
|
@ -9092,6 +9132,15 @@ pretty-format@^27.0.0, pretty-format@^27.2.4:
|
|||
ansi-styles "^5.0.0"
|
||||
react-is "^17.0.1"
|
||||
|
||||
pretty-format@^29.6.2:
|
||||
version "29.6.2"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.6.2.tgz#3d5829261a8a4d89d8b9769064b29c50ed486a47"
|
||||
integrity sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg==
|
||||
dependencies:
|
||||
"@jest/schemas" "^29.6.0"
|
||||
ansi-styles "^5.0.0"
|
||||
react-is "^18.0.0"
|
||||
|
||||
process-nextick-args@~2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz"
|
||||
|
@ -9396,6 +9445,11 @@ react-is@^17.0.1:
|
|||
resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz"
|
||||
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
|
||||
|
||||
react-is@^18.0.0:
|
||||
version "18.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
|
||||
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
|
||||
|
||||
react-lifecycles-compat@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz"
|
||||
|
|
Loading…
Reference in New Issue