more on parser

This commit is contained in:
adueck 2023-08-17 18:12:09 +04:00
parent b384771db5
commit 288718f69a
12 changed files with 1064 additions and 283 deletions

View File

@ -1,21 +0,0 @@
import * as T from "../../../types";
export function parseBa(
tokens: Readonly<T.Token[]>
): T.ParseResult<{ type: "ba" }>[] {
if (!tokens.length) {
return [];
}
const [first, ...rest] = tokens;
if (first.s === "به") {
return [
{
body: {
type: "ba",
},
errors: [],
tokens: rest,
},
];
} else return [];
}

View File

@ -0,0 +1,61 @@
import * as T from "../../../types";
import { fmapParseResult } from "../fp-ps";
import { parseNP } from "./parse-np";
import { parseVerb } from "./parse-verb";
export function parseBlock(
tokens: Readonly<T.Token[]>,
lookup: (s: Partial<T.DictionaryEntry>) => T.DictionaryEntry[],
verbLookup: (s: string) => T.VerbEntry[]
): T.ParseResult<
| [
{
inflected: boolean;
selection: T.NPSelection;
}
]
| [
(
| {
type: "PH";
s: string;
}
| undefined
),
Omit<T.VBE, "ps">
]
| []
>[] {
if (tokens.length === 0) {
return [
{
tokens: [],
body: [],
errors: [],
},
];
}
return [
...(fmapParseResult((x) => [x], parseNP(tokens, lookup)) as T.ParseResult<
[
{
inflected: boolean;
selection: T.NPSelection;
}
]
>[]),
...(parseVerb(tokens, verbLookup) as T.ParseResult<
[
(
| {
type: "PH";
s: string;
}
| undefined
),
Omit<T.VBE, "ps">
]
>[]),
];
}

View File

@ -0,0 +1,96 @@
import * as T from "../../../types";
import { parseBlock } from "./parse-block";
import { parseKidsSection } from "./parse-kids-section";
import { bindParseResult, returnParseResult } from "./utils";
export function parseBlocks(
tokens: Readonly<T.Token[]>,
lookup: (s: Partial<T.DictionaryEntry>) => T.DictionaryEntry[],
verbLookup: (s: string) => T.VerbEntry[],
prevBlocks: (
| {
inflected: boolean;
selection: T.NPSelection;
}
| {
type: "PH";
s: string;
}
| Omit<T.VBE, "ps">
)[],
kids: T.ParsedKid[]
): T.ParseResult<{
kids: T.ParsedKid[];
blocks: (
| {
inflected: boolean;
selection: T.NPSelection;
}
| {
type: "PH";
s: string;
}
| Omit<T.VBE, "ps">
)[];
}>[] {
if (tokens.length === 0) {
// console.log("at end", { prevBlocks, kids });
return returnParseResult(tokens, { blocks: prevBlocks, kids });
}
const block = parseBlock(tokens, lookup, verbLookup);
const kidsR = parseKidsSection(tokens, []);
const allResults = [...block, ...kidsR] as T.ParseResult<
| [
{
inflected: boolean;
selection: T.NPSelection;
}
]
| [
(
| {
type: "PH";
s: string;
}
| undefined
),
Omit<T.VBE, "ps">
]
| []
| { kids: T.ParsedKid[] }
>[];
if (!allResults.length) {
return [
{
tokens: [],
body: { blocks: prevBlocks, kids },
errors: [],
},
];
}
return bindParseResult(allResults, (tokens, r) => {
if ("kids" in r) {
return {
next: parseBlocks(tokens, lookup, verbLookup, prevBlocks, [
...kids,
...r.kids,
]),
errors:
prevBlocks.length !== 1
? [{ message: "kids' section out of place" }]
: [],
};
}
// filter out the empty PH pieces
// for some reason ts won't let me do filter here
const newBlocks = r.flatMap((x) => (x ? [x] : []));
return parseBlocks(
tokens,
lookup,
verbLookup,
[...prevBlocks, ...newBlocks],
kids
);
});
}

View File

@ -0,0 +1,27 @@
import * as T from "../../../types";
import { returnParseResult } from "./utils";
export function parseKid(
tokens: Readonly<T.Token[]>
): T.ParseResult<T.ParsedKid>[] {
if (tokens.length === 0) {
return [];
}
const [{ s }, ...rest] = tokens;
if (s === "به") {
return returnParseResult(rest, "ba");
}
if (s === "یې") {
return returnParseResult(rest, "ye");
}
if (s === "مې") {
return returnParseResult(rest, "me");
}
if (s === "دې") {
return returnParseResult(rest, "de");
}
if (s === "مو") {
return returnParseResult(rest, "mU");
}
return [];
}

View File

@ -0,0 +1,83 @@
/* eslint-disable jest/no-conditional-expect */
/* eslint-disable jest/valid-title */
import * as T from "../../../types";
import { parseKidsSection } from "./parse-kids-section";
import { tokenizer } from "./tokenizer";
const tests: {
label: string;
cases: {
input: string;
output: T.ParsedKid[];
error?: boolean;
}[];
}[] = [
{
label: "basic kids section",
cases: [
{
input: "به",
output: ["ba"],
},
{
input: "به دې",
output: ["ba", "de"],
},
{
input: "",
output: [],
},
{
input: "مې دې یې",
output: ["me", "de", "ye"],
},
{
input: "دې به مې",
output: ["de", "ba", "me"],
error: true,
},
{
input: "مې یې",
output: ["me", "ye"],
},
{
input: "دې مې",
output: ["de", "me"],
error: true,
},
],
},
{
label: "can parse kids section when tokens come after",
cases: [
{
input: "به سړی",
output: ["ba"],
},
{
input: "مې دې واخیسته",
output: ["me", "de"],
},
],
},
];
tests.forEach(({ label, cases }) => {
test(label, () => {
cases.forEach(({ input, output, error }) => {
const tokens = tokenizer(input);
const parsed = parseKidsSection(tokens, []);
if (output.length) {
expect(parsed.length).toBe(1);
expect(parsed.map((x) => x.body.kids)).toEqual(
output.length ? [output] : []
);
if (error) {
expect(parsed[0].errors.length).toBeTruthy();
} else {
expect(parsed[0].errors.length).toBe(0);
}
}
});
});
});

View File

@ -0,0 +1,50 @@
import * as T from "../../../types";
import { parseKid } from "./parse-kid";
import { bindParseResult, returnParseResult } from "./utils";
export function parseKidsSection(
tokens: Readonly<T.Token[]>,
prevKids: T.ParsedKid[]
): T.ParseResult<{ kids: T.ParsedKid[] }>[] {
if (tokens.length === 0) {
return prevKids.length ? returnParseResult(tokens, { kids: prevKids }) : [];
}
const parsedKid = parseKid(tokens);
// TODO: is this even necessary ??
if (!parsedKid.length) {
return prevKids.length ? returnParseResult(tokens, { kids: prevKids }) : [];
}
return bindParseResult(parsedKid, (tokens, r) => {
// return parseKidsSection(tokens, [...prevKids, r]);
return {
errors: kidDoubled(r, prevKids)
? [{ message: `double '${r}' in kids section` }]
: !kidComesBehind(r, prevKids.at(-1))
? [{ message: "kids section out of order" }]
: [],
next: parseKidsSection(tokens, [...prevKids, r]),
};
});
}
function kidDoubled(k: T.ParsedKid, prev: T.ParsedKid[]): boolean {
return !!prev.find((x) => x === k);
}
const kidsOrder: T.ParsedKid[] = ["ba", "me", "de", "ye"];
function getKidRank(k: T.ParsedKid): number {
if (k === "mU") {
return 1;
}
return kidsOrder.indexOf(k);
}
function kidComesBehind(
k: T.ParsedKid,
prev: T.ParsedKid | undefined
): boolean {
if (!prev) {
return true;
}
return getKidRank(k) >= getKidRank(prev);
}

View File

@ -80,7 +80,7 @@ export function parsePronoun(tokens: Readonly<T.Token[]>): T.ParseResult<{
inflected: false,
selection: {
type: "pronoun",
person: 4,
person: 5,
distance: "far",
},
},

View File

@ -12,6 +12,11 @@ import {
// big problem ما سړی یوړ crashes it !!
// TODO: کول verbs!
// check that aawu stuff is working
// check oo`azmooy -
// check څاته
export function parseVerb(
tokens: Readonly<T.Token[]>,
verbLookup: (s: string) => T.VerbEntry[]

View File

@ -1,35 +1,23 @@
import * as T from "../../../types";
import { parseNP } from "./parse-np";
import { bindParseResult } from "./utils";
import { parseVerb } from "./parse-verb";
import { bindParseResult, returnParseResult } from "./utils";
import {
makeObjectSelectionComplete,
makeSubjectSelectionComplete,
} from "../phrase-building/blocks-utils";
import { vEntry } from "../new-verb-engine/rs-helpers";
import { getPersonFromNP, isThirdPerson } from "../phrase-building/vp-tools";
import { parseBa } from "./parse-ba";
import { getPersonFromNP, isPastTense } from "../phrase-building/vp-tools";
import { parseBlocks } from "./parse-blocks";
import { makePronounSelection } from "../phrase-building/make-selections";
import { isFirstOrSecondPersPronoun } from "../phrase-building/render-vp";
// to hide equatives type-doubling issue
const kedulStat = vEntry({
ts: 1581086654898,
i: 11100,
p: "کېدل",
f: "kedul",
g: "kedul",
e: "to become _____",
r: 2,
c: "v. intrans.",
ssp: "ش",
ssf: "sh",
prp: "شول",
prf: "shwul",
pprtp: "شوی",
pprtf: "shúway",
noOo: true,
ec: "become",
});
// cool examples: زه خوږې ماشومې وهم
// cool examples:
// زه خوږې ماشومې وهم
// ماشومان سړي ولیدل
// ماشومانو سړي ولیدل
// make impossible subjects like I saw me, error
// PROBLEM! ته وینې doesn't work cause it just takes ته as a verb phrase ?
export function parseVP(
tokens: Readonly<T.Token[]>,
@ -39,127 +27,59 @@ export function parseVP(
if (tokens.length === 0) {
return [];
}
// how to make this into a nice pipeline... 🤔
const NP1 = parseNP(tokens, lookup).filter(({ errors }) => !errors.length);
const ba = bindParseResult(NP1, (tokens, np1) => {
const b = parseBa(tokens);
if (!b.length) {
return [
{
tokens,
body: {
np1,
ba: false,
},
errors: [],
},
];
} else {
return b.map(({ tokens, errors }) => ({
body: {
np1,
ba: true,
},
errors,
tokens,
}));
}
});
const NP2 = bindParseResult<
{
np1: {
inflected: boolean;
selection: T.NPSelection;
};
ba: boolean;
},
{
np1: {
inflected: boolean;
selection: T.NPSelection;
};
ba: boolean;
np2:
const blocks = parseBlocks(tokens, lookup, verbLookup, [], []);
return bindParseResult(blocks, (tokens, { blocks, kids }) => {
const ph = blocks.find((x) => "type" in x && x.type === "PH") as
| {
inflected: boolean;
selection: T.NPSelection;
type: "PH";
s: string;
}
| undefined;
const verb = blocks.find((x) => "type" in x && x.type === "VB") as
| Omit<T.VBE, "ps">
| undefined;
const ba = !!kids.find((k) => k === "ba");
if (!verb || verb.type !== "VB" || verb.info.type !== "verb") {
return [];
}
>(ba, (tokens, { np1, ba }) => {
const np2s = parseNP(tokens, lookup);
if (!np2s.length) {
const r: T.ParseResult<{
np1: {
inflected: boolean;
selection: T.NPSelection;
if (verb.info.aspect === "perfective") {
// TODO: check that the perfective head is in the right place and actually matches
if (!ph) {
return [];
}
}
const tense = getTenseFromRootsStems(ba, verb.info.base, verb.info.aspect);
const isPast = isPastTense(tense);
const nps = blocks.filter(
(x): x is { inflected: boolean; selection: T.NPSelection } =>
"inflected" in x
);
// TODO: check that verb and PH match
if (verb.info.verb.entry.c.includes("intrans")) {
if (nps.length > 1) {
return [];
}
if (nps.length === 0) {
const v: T.VerbSelectionComplete = {
type: "verb",
verb: verb.info.verb,
transitivity: "intransitive",
canChangeTransitivity: false,
canChangeStatDyn: false,
negative: false,
tense,
canChangeVoice: true,
isCompound: false,
voice: "active",
};
ba: boolean;
np2: undefined;
}>[] = [
{
tokens,
body: {
np1,
np2: undefined,
ba,
},
errors: [],
},
];
return r;
}
return np2s.map((p) => ({
tokens: p.tokens,
body: {
np1,
np2: p.body,
ba,
},
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,
ba: nps.ba,
},
errors: p.errors,
}));
}).filter(({ errors }) => !errors.length);
// TODO: be able to bind mulitple vals
return bindParseResult(vb, (tokens, { np1, np2, v: [ph, v], ba }) => {
const w: T.ParseResult<T.VPSelectionComplete>[] = [];
if (v.info.type === "equative") {
throw new Error("not yet implemented");
}
const isPast = v.info.base === "root";
const intransitive =
v.info.type === "verb" && v.info.verb.entry.c.includes("intrans.");
if (intransitive) {
if (np2) return [];
const s = np1;
const errors: T.ParseError[] = [];
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),
block: makeSubjectSelectionComplete({
type: "NP",
selection: makePronounSelection(verb.person),
}),
},
{
key: 2,
@ -169,91 +89,268 @@ export function parseVP(
},
},
];
const verb: T.VerbSelectionComplete = {
return returnParseResult(tokens, {
blocks,
verb: v,
externalComplement: undefined,
form: {
removeKing: true,
shrinkServant: false,
},
} as T.VPSelectionComplete);
}
if (nps.length === 1) {
const errors: T.ParseError[] = [];
if (getPersonFromNP(nps[0].selection) !== verb.person) {
errors.push({ message: "subject must agree with intransitive verb" });
}
if (nps[0].inflected) {
errors.push({
message: "subject of intransitive verb must not be inflected",
});
}
const blocks: T.VPSBlockComplete[] = [
{
key: 1,
block: makeSubjectSelectionComplete(nps[0].selection),
},
{
key: 2,
block: {
type: "objectSelection",
selection: "none",
},
},
];
const v: T.VerbSelectionComplete = {
type: "verb",
verb: v.info.type === "verb" ? v.info.verb : kedulStat,
verb: verb.info.verb,
transitivity: "intransitive",
canChangeTransitivity: false,
canChangeStatDyn: false,
negative: false,
tense: getTenseFromRootsStems(ba, v.info.base, v.info.aspect),
tense,
canChangeVoice: true,
isCompound: false,
voice: "active",
};
w.push({
return returnParseResult(
tokens,
body: {
{
blocks,
verb,
verb: v,
externalComplement: undefined,
form: {
removeKing: false,
shrinkServant: false,
},
} as T.VPSelectionComplete,
errors
);
}
} else {
// transitive
if (nps.length > 2) {
return [];
}
if (nps.length === 0) {
return [];
}
if (nps.length === 1) {
const np = nps[0];
return (
[
{
removeKing: true,
shrinkServant: false,
},
errors,
});
} else {
// transitive verb
if (!(np1 && np2)) return [];
[[np1, np2, false] as const, [np2, np1, true] as const].forEach(
([s, o, reversed]) => {
if (v.info.type === "equative") {
throw new Error("not yet implemented");
}
if (!s || !o) return [];
// TODO: check if perfective head MATCHES verb
if (v.info.aspect === "perfective" && !ph) {
return [];
}
const subjPerson = getPersonFromNP(s.selection);
{
removeKing: false,
shrinkServant: true,
},
] as const
).flatMap((form) => {
const errors: T.ParseError[] = [];
if (intransitive) {
if (form.removeKing) {
// king is gone
// servant is there
const king: T.NPSelection = {
type: "NP",
selection: makePronounSelection(verb.person),
};
const servant = np.selection;
if (!isPast) {
if (isFirstOrSecondPersPronoun(np.selection))
if (!np.inflected) {
errors.push({
message:
"first or second pronoun object of non-past transitive verb must be inflected",
});
}
} else {
if (!np.inflected) {
errors.push({
message:
"object of non-past transitive verb must not be inflected",
});
}
}
const blocks: T.VPSBlockComplete[] = !isPast
? [
{
key: 1,
block: makeSubjectSelectionComplete(king),
},
{
key: 2,
block: makeObjectSelectionComplete(servant),
},
]
: [
{
key: 1,
block: makeSubjectSelectionComplete(servant),
},
{
key: 2,
block: makeObjectSelectionComplete(king),
},
];
const v: T.VerbSelectionComplete = {
type: "verb",
// @ts-ignore
verb: verb.info.verb,
transitivity: "transitive",
canChangeTransitivity: false,
canChangeStatDyn: false,
negative: false,
tense,
canChangeVoice: true,
isCompound: false,
voice: "active",
};
return returnParseResult(
tokens,
{
blocks,
verb: v,
externalComplement: undefined,
form,
} as T.VPSelectionComplete,
errors
);
} else {
// servant is shrunken
// king is there
const king = np.selection;
const shrunkenServantPeople = getPeopleFromKids(kids);
if (!shrunkenServantPeople.length) {
return [];
}
if (isPast) {
if (getPersonFromNP(o.selection) !== v.person) {
const servants = shrunkenServantPeople.map(
(person): T.NPSelection => ({
type: "NP",
selection: makePronounSelection(person),
})
);
if (!isPast) {
if (np.inflected) {
errors.push({
message: "transitive past tense verb does not match object",
message:
"object of a past tense transitive verb should not be inflected",
});
}
} else {
if (np.inflected) {
errors.push({
message:
"subject of a non-past tense transitive verb should not be inflected",
});
}
}
const blocksOps: T.VPSBlockComplete[][] = servants.map((servant) =>
!isPast
? [
{
key: 1,
block: makeSubjectSelectionComplete(king),
},
{
key: 2,
block: makeObjectSelectionComplete(servant),
},
]
: [
{
key: 1,
block: makeSubjectSelectionComplete(servant),
},
{
key: 2,
block: makeObjectSelectionComplete(king),
},
]
);
const v: T.VerbSelectionComplete = {
type: "verb",
// @ts-ignore
verb: verb.info.verb,
transitivity: "transitive",
canChangeTransitivity: false,
canChangeStatDyn: false,
negative: false,
tense,
canChangeVoice: true,
isCompound: false,
voice: "active",
};
return blocksOps.map((blocks) => ({
tokens,
body: {
blocks,
verb: v,
externalComplement: undefined,
form,
} as T.VPSelectionComplete,
errors,
}));
}
});
// possibilities
// present:
// - no king (np is servant)
// - shrunken servant (np is king)
// past:
// - no king (np is servant)
// - shrunken servant (np is king)
} else {
if (isPast) {
return (
[
[nps[0], nps[1], false],
[nps[1], nps[0], true],
] as const
).flatMap(([s, o, flip]) => {
const errors: T.ParseError[] = [];
if (!s.inflected) {
errors.push({
message: "transitive past tense subject should be inflected",
message:
"subject of transitive past tense verb must be inflected",
});
}
if (o.inflected) {
errors.push({
message:
"transitive past tense object should not be inflected",
"object of past tense transitive verb must not be inflected",
});
}
}
} else {
if (getPersonFromNP(s.selection) !== v.person) {
errors.push({
message: "verb does not match subject",
});
} else {
if (s.inflected) {
errors.push({ message: "subject should not be inflected" });
}
if (o.selection.selection.type === "pronoun") {
if (!isThirdPerson(subjPerson) && !o.inflected) {
if (getPersonFromNP(o.selection) !== verb.person) {
errors.push({
message:
"1st or 2nd person object pronoun should be inflected",
"past tense transitive verb must agree with the object",
});
}
} else if (o.inflected) {
errors.push({ message: "object should not be inflected" });
}
}
}
const blocks: T.VPSBlockComplete[] = [
let blocks: T.VPSBlockComplete[] = [
{
key: 1,
block: makeSubjectSelectionComplete(s.selection),
@ -263,41 +360,398 @@ export function parseVP(
block: makeObjectSelectionComplete(o.selection),
},
];
if (reversed) {
blocks.reverse();
if (flip) {
blocks = blocks.reverse();
}
const verb: T.VerbSelectionComplete = {
const v: T.VerbSelectionComplete = {
type: "verb",
verb: v.info.type === "verb" ? v.info.verb : kedulStat,
// @ts-ignore
verb: verb.info.verb,
transitivity: "transitive",
canChangeTransitivity: false,
canChangeStatDyn: false,
negative: false,
tense: getTenseFromRootsStems(ba, v.info.base, v.info.aspect),
tense,
canChangeVoice: true,
isCompound: false,
voice: "active",
};
w.push({
return returnParseResult(
tokens,
body: {
{
blocks,
verb,
verb: v,
externalComplement: undefined,
form: {
removeKing: false,
shrinkServant: false,
},
},
errors,
} as T.VPSelectionComplete,
errors
);
});
} else {
return (
[
[nps[0], nps[1], false],
[nps[1], nps[0], true],
] as const
).flatMap(([s, o, flip]) => {
const errors: T.ParseError[] = [];
if (isFirstOrSecondPersPronoun(o.selection)) {
if (!o.inflected) {
errors.push({
message:
"object of transitive non-past tense verb must be inflected when it's a first or second person pronoun",
});
}
);
} else {
if (o.inflected) {
errors.push({
message:
"object of transitive non-past tense verb must not be inflected",
});
}
return w;
}
if (s.inflected) {
errors.push({
message:
"subject of transitive non-past tense verb must not be inflected",
});
}
if (getPersonFromNP(s.selection) !== verb.person) {
errors.push({
message:
"non-past tense transitive verb must agree with the subject",
});
}
let blocks: T.VPSBlockComplete[] = [
{
key: 1,
block: makeSubjectSelectionComplete(s.selection),
},
{
key: 2,
block: makeObjectSelectionComplete(o.selection),
},
];
if (flip) {
blocks = blocks.reverse();
}
const v: T.VerbSelectionComplete = {
type: "verb",
// @ts-ignore
verb: verb.info.verb,
transitivity: "transitive",
canChangeTransitivity: false,
canChangeStatDyn: false,
negative: false,
tense,
canChangeVoice: true,
isCompound: false,
voice: "active",
};
return returnParseResult(
tokens,
{
blocks,
verb: v,
externalComplement: undefined,
form: {
removeKing: false,
shrinkServant: false,
},
} as T.VPSelectionComplete,
errors
);
});
}
}
}
return [];
});
}
function getPeopleFromKids(kids: T.ParsedKid[]): T.Person[] {
const p: T.Person[] = [];
for (let k of kids) {
if (k === "me") {
p.push(T.Person.FirstSingMale);
p.push(T.Person.FirstSingFemale);
} else if (k === "de") {
p.push(T.Person.SecondSingMale);
p.push(T.Person.SecondSingFemale);
} else if (k === "ye") {
p.push(T.Person.ThirdSingMale);
p.push(T.Person.ThirdSingFemale);
p.push(T.Person.ThirdPlurMale);
p.push(T.Person.ThirdPlurFemale);
} else if (k === "mU") {
p.push(T.Person.FirstPlurMale);
p.push(T.Person.FirstPlurFemale);
p.push(T.Person.SecondPlurMale);
p.push(T.Person.SecondPlurFemale);
}
}
return p;
}
// // how to make this into a nice pipeline... 🤔
// const NP1 = parseNP(tokens, lookup).filter(({ errors }) => !errors.length);
// const ba = bindParseResult(NP1, (tokens, np1) => {
// const b = parseBa(tokens);
// if (!b.length) {
// return [
// {
// tokens,
// body: {
// np1,
// ba: false,
// },
// errors: [],
// },
// ];
// } else {
// return b.map(({ tokens, errors }) => ({
// body: {
// np1,
// ba: true,
// },
// errors,
// tokens,
// }));
// }
// });
// const NP2 = bindParseResult<
// {
// np1: {
// inflected: boolean;
// selection: T.NPSelection;
// };
// ba: boolean;
// },
// {
// np1: {
// inflected: boolean;
// selection: T.NPSelection;
// };
// ba: boolean;
// np2:
// | {
// inflected: boolean;
// selection: T.NPSelection;
// }
// | undefined;
// }
// >(ba, (tokens, { np1, ba }) => {
// const np2s = parseNP(tokens, lookup);
// if (!np2s.length) {
// const r: T.ParseResult<{
// np1: {
// inflected: boolean;
// selection: T.NPSelection;
// };
// ba: boolean;
// np2: undefined;
// }>[] = [
// {
// tokens,
// body: {
// np1,
// np2: undefined,
// ba,
// },
// errors: [],
// },
// ];
// return r;
// }
// return np2s.map((p) => ({
// tokens: p.tokens,
// body: {
// np1,
// np2: p.body,
// ba,
// },
// 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,
// ba: nps.ba,
// },
// errors: p.errors,
// }));
// }).filter(({ errors }) => !errors.length);
// // TODO: be able to bind mulitple vals
// return bindParseResult(vb, (tokens, { np1, np2, v: [ph, v], ba }) => {
// const w: T.ParseResult<T.VPSelectionComplete>[] = [];
// if (v.info.type === "equative") {
// throw new Error("not yet implemented");
// }
// const isPast = v.info.base === "root";
// const intransitive =
// v.info.type === "verb" && v.info.verb.entry.c.includes("intrans.");
// if (intransitive) {
// if (np2) return [];
// const s = np1;
// const errors: T.ParseError[] = [];
// 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: getTenseFromRootsStems(ba, v.info.base, v.info.aspect),
// canChangeVoice: true,
// isCompound: false,
// voice: "active",
// };
// w.push({
// tokens,
// body: {
// blocks,
// verb,
// externalComplement: undefined,
// form: {
// removeKing: false,
// shrinkServant: false,
// },
// },
// errors,
// });
// } else {
// // transitive verb
// if (!(np1 && np2)) return [];
// [[np1, np2, false] as const, [np2, np1, true] as const].forEach(
// ([s, o, reversed]) => {
// if (v.info.type === "equative") {
// throw new Error("not yet implemented");
// }
// if (!s || !o) return [];
// // TODO: check if perfective head MATCHES verb
// if (v.info.aspect === "perfective" && !ph) {
// return [];
// }
// 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",
// });
// } else {
// if (s.inflected) {
// errors.push({ message: "subject should not be inflected" });
// }
// if (o.selection.selection.type === "pronoun") {
// if (!isThirdPerson(subjPerson) && !o.inflected) {
// errors.push({
// message:
// "1st or 2nd person object pronoun should be inflected",
// });
// }
// } else if (o.inflected) {
// errors.push({ message: "object should not be inflected" });
// }
// }
// }
// 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: getTenseFromRootsStems(ba, v.info.base, v.info.aspect),
// canChangeVoice: true,
// isCompound: false,
// voice: "active",
// };
// w.push({
// tokens,
// body: {
// blocks,
// verb,
// externalComplement: undefined,
// form: {
// removeKing: false,
// shrinkServant: false,
// },
// },
// errors,
// });
// }
// );
// }
function getTenseFromRootsStems(
hasBa: boolean,
base: "root" | "stem",

View File

@ -20,7 +20,7 @@ import * as T from "../../../types";
* from the different previous results
* @returns
*/
export function bindParseResult<C extends object, D extends object>(
export function bindParseResult<C, D>(
previous: T.ParseResult<C>[],
f: (
tokens: Readonly<T.Token[]>,
@ -59,18 +59,42 @@ export function bindParseResult<C extends object, D extends object>(
errors: [...errsPassed, ...x.errors, ...errors],
}));
});
return cleanOutFails(nextPossibilities);
return cleanOutResults(nextPossibilities);
}
export function cleanOutFails<C extends object>(
export function returnParseResult<D>(
tokens: Readonly<T.Token[]>,
body: D,
errors?: T.ParseError[]
): T.ParseResult<D>[] {
return [
{
tokens,
body,
errors: errors || [],
},
];
}
/**
* finds the most successful path(s) and culls out any other more erroneous
* or redundant paths
*/
export function cleanOutResults<C>(
results: T.ParseResult<C>[]
): T.ParseResult<C>[] {
// if there's any success anywhere, remove any of the errors
const errorsGone = results.find((x) => x.errors.length === 0)
? results.filter((x) => x.errors.length === 0)
: results;
if (results.length === 0) {
return results;
}
let min = Infinity;
for (let a of results) {
if (a.errors.length < min) {
min = a.errors.length;
}
}
const errorsCulled = results.filter((x) => x.errors.length === min);
// @ts-ignore
return Array.from(new Set(errorsGone.map(JSON.stringify))).map(JSON.parse);
return Array.from(new Set(errorsCulled.map(JSON.stringify))).map(JSON.parse);
}
export function isCompleteResult<C extends object>(

View File

@ -352,7 +352,7 @@ export function getKingAndServant(
};
}
function isFirstOrSecondPersPronoun(
export function isFirstOrSecondPersPronoun(
o: "none" | T.NPSelection | T.Person.ThirdPlurMale
): boolean {
if (typeof o !== "object") return false;

View File

@ -1197,6 +1197,8 @@ export type Kid = {
kid: { type: "ba" } | MiniPronoun;
};
export type ParsedKid = "ba" | "me" | "de" | "ye" | "mU";
export type MiniPronoun = {
type: "mini-pronoun";
person: Person;