working towards parsing perfect verbs

This commit is contained in:
adueck 2023-09-01 16:35:24 +04:00
parent 28f79729a4
commit 56770b5041
6 changed files with 1111 additions and 491 deletions

View File

@ -1,8 +1,10 @@
import * as T from "../../../types"; import * as T from "../../../types";
import { LookupFunction } from "./lookup"; import { LookupFunction } from "./lookup";
import { parseEquative } from "./parse-equative";
import { parseKidsSection } from "./parse-kids-section"; import { parseKidsSection } from "./parse-kids-section";
import { parseNeg } from "./parse-negative"; import { parseNeg } from "./parse-negative";
import { parseNP } from "./parse-np"; import { parseNP } from "./parse-np";
import { parsePastPart } from "./parse-past-part";
import { parsePH } from "./parse-ph"; import { parsePH } from "./parse-ph";
import { parseVerb } from "./parse-verb"; import { parseVerb } from "./parse-verb";
import { bindParseResult, returnParseResult } from "./utils"; import { bindParseResult, returnParseResult } from "./utils";
@ -26,6 +28,8 @@ export function parseBlocks(
const np = prevPh ? [] : parseNP(tokens, lookup); const np = prevPh ? [] : parseNP(tokens, lookup);
const ph = vbExists || prevPh ? [] : parsePH(tokens); const ph = vbExists || prevPh ? [] : parsePH(tokens);
const vb = parseVerb(tokens, lookup); const vb = parseVerb(tokens, lookup);
const vbp = parsePastPart(tokens, lookup);
const eq = parseEquative(tokens);
const neg = parseNeg(tokens); const neg = parseNeg(tokens);
const kidsR = parseKidsSection(tokens, []); const kidsR = parseKidsSection(tokens, []);
const allResults: T.ParseResult<T.ParsedBlock | T.ParsedKidsSection>[] = [ const allResults: T.ParseResult<T.ParsedBlock | T.ParsedKidsSection>[] = [
@ -33,6 +37,8 @@ export function parseBlocks(
...ph, ...ph,
...neg, ...neg,
...vb, ...vb,
...vbp,
...eq,
...kidsR, ...kidsR,
]; ];
// TODO: is this necessary? // TODO: is this necessary?
@ -76,7 +82,10 @@ export function parseBlocks(
}); });
} }
function phMatches(ph: T.ParsedPH | undefined, vb: T.ParsedVBE | undefined) { function phMatches(
ph: T.ParsedPH | undefined,
vb: T.ParsedVBE | T.ParsedVBP | undefined
) {
if (!ph) { if (!ph) {
return true; return true;
} }

View File

@ -0,0 +1,106 @@
import * as T from "../../../types";
export function parseEquative(
tokens: Readonly<T.Token[]>
): T.ParseResult<T.ParsedVBE>[] {
if (tokens.length === 0) {
return [];
}
const [{ s }, ...rest] = tokens;
const match = table.find((x) => x.ps.includes(s));
if (!match) {
return [];
}
return match.people.flatMap((person) =>
match.tenses.map((tense) => ({
tokens: rest,
body: {
type: "VB",
info: {
type: "equative",
tense,
},
person,
},
errors: [],
}))
);
}
// TODO: NOT COMPLETE / CORRECT
const table: {
ps: string[];
tenses: T.EquativeTenseWithoutBa[];
people: T.Person[];
}[] = [
{
ps: ["یم"],
tenses: ["present", "habitual"],
people: [T.Person.FirstSingMale, T.Person.FirstSingFemale],
},
{
ps: ["یې"],
tenses: ["present", "habitual"],
people: [T.Person.SecondSingMale, T.Person.SecondSingFemale],
},
{
ps: ["یو"],
tenses: ["present", "habitual"],
people: [T.Person.FirstPlurMale, T.Person.FirstPlurFemale],
},
{
ps: ["یئ"],
tenses: ["present", "habitual"],
people: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale],
},
{
ps: ["وم"],
tenses: ["subjunctive", "past"],
people: [T.Person.FirstSingMale, T.Person.FirstSingFemale],
},
{
ps: ["وې"],
tenses: ["subjunctive", "past"],
people: [T.Person.SecondSingMale, T.Person.SecondSingFemale],
},
{
ps: ["وو"],
tenses: ["subjunctive", "past"],
people: [T.Person.FirstPlurMale, T.Person.FirstPlurFemale],
},
{
ps: ["وئ"],
tenses: ["subjunctive", "past"],
people: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale],
},
{
ps: ["دی"],
tenses: ["present"],
people: [T.Person.ThirdSingMale],
},
{
ps: ["ده"],
tenses: ["present"],
people: [T.Person.ThirdSingFemale],
},
{
ps: ["دي"],
tenses: ["present"],
people: [T.Person.ThirdPlurMale, T.Person.ThirdPlurFemale],
},
{
ps: ["وي"],
tenses: ["habitual"],
people: [
T.Person.ThirdSingMale,
T.Person.ThirdSingFemale,
T.Person.ThirdPlurMale,
T.Person.ThirdPlurFemale,
],
},
{
ps: ["وای", "وی"],
tenses: ["pastSubjunctive"],
people: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
},
];

View File

@ -0,0 +1,64 @@
import * as T from "../../../types";
import { LookupFunction } from "./lookup";
import { returnParseResult } from "./utils";
export function parsePastPart(
tokens: Readonly<T.Token[]>,
lookup: LookupFunction
): T.ParseResult<T.ParsedVBP>[] {
if (tokens.length === 0) {
return [];
}
const [{ s }, ...rest] = tokens;
const ending: "ی" | "ي" | "ې" = s.at(-1) as "ی" | "ي" | "ې";
if (!ending || !["ی", "ي", "ې"].includes(ending)) {
return [];
}
// TODO: ALSO HANDLE SHORT FORMS
const wOutEnd = s.slice(0, -1);
const matches = lookup(wOutEnd, "participle");
const genNums = endingGenderNum(ending);
return matches
.flatMap<T.ParsedVBP>((verb) =>
genNums.map<T.ParsedVBP>((genNum) => ({
type: "VB",
info: {
type: "ppart",
verb,
genNum,
},
}))
)
.flatMap((m) => returnParseResult(rest, m));
}
function endingGenderNum(ending: "ی" | "ي" | "ې"): T.GenderNumber[] {
if (ending === "ی") {
return [
{
gender: "masc",
number: "singular",
},
];
}
if (ending === "ي") {
return [
{
gender: "masc",
number: "plural",
},
];
}
// if (ending === "ې") {
return [
{
gender: "fem",
number: "singular",
},
{
gender: "fem",
number: "plural",
},
];
// }
}

View File

@ -22,6 +22,7 @@ const leedul = wordQuery("لیدل", "verb");
const kenaastul = wordQuery("کېناستل", "verb"); const kenaastul = wordQuery("کېناستل", "verb");
const wurul = wordQuery("وړل", "verb"); const wurul = wordQuery("وړل", "verb");
const akheestul = wordQuery("اخیستل", "verb"); const akheestul = wordQuery("اخیستل", "verb");
const khandul = wordQuery("خندل", "verb");
const tests: { const tests: {
label: string; label: string;
@ -51,6 +52,11 @@ const tests: {
output: [], output: [],
error: true, error: true,
}, },
{
input: "تلم مې",
output: [],
error: true,
},
{ {
input: "سړی زه ویني", input: "سړی زه ویني",
output: [], output: [],
@ -1381,6 +1387,258 @@ const tests: {
}, },
], ],
}, },
{
label: "grammatically transitive",
cases: [
{
input: "زه خاندم",
output: getPeople(1, "sing").map<T.VPSelectionComplete>((person) => ({
blocks: [
{
key: 1,
block: makeSubjectSelectionComplete({
type: "NP",
selection: makePronounSelection(person),
}),
},
{
key: 2,
block: {
type: "objectSelection",
selection: T.Person.ThirdPlurMale,
},
},
],
verb: {
type: "verb",
verb: khandul,
transitivity: "grammatically transitive",
canChangeTransitivity: false,
canChangeStatDyn: false,
negative: false,
tense: "presentVerb",
canChangeVoice: true,
isCompound: false,
voice: "active",
},
externalComplement: undefined,
form: {
removeKing: false,
shrinkServant: false,
},
})),
},
{
input: "خاندم",
output: getPeople(1, "sing").map<T.VPSelectionComplete>((person) => ({
blocks: [
{
key: 1,
block: makeSubjectSelectionComplete({
type: "NP",
selection: makePronounSelection(person),
}),
},
{
key: 2,
block: {
type: "objectSelection",
selection: T.Person.ThirdPlurMale,
},
},
],
verb: {
type: "verb",
verb: khandul,
transitivity: "grammatically transitive",
canChangeTransitivity: false,
canChangeStatDyn: false,
negative: false,
tense: "presentVerb",
canChangeVoice: true,
isCompound: false,
voice: "active",
},
externalComplement: undefined,
form: {
removeKing: true,
shrinkServant: false,
},
})),
},
{
input: "ما خندل",
output: getPeople(1, "sing").map<T.VPSelectionComplete>((person) => ({
blocks: [
{
key: 1,
block: makeSubjectSelectionComplete({
type: "NP",
selection: makePronounSelection(person),
}),
},
{
key: 2,
block: {
type: "objectSelection",
selection: T.Person.ThirdPlurMale,
},
},
],
verb: {
type: "verb",
verb: khandul,
transitivity: "grammatically transitive",
canChangeTransitivity: false,
canChangeStatDyn: false,
negative: false,
tense: "imperfectivePast",
canChangeVoice: true,
isCompound: false,
voice: "active",
},
externalComplement: undefined,
form: {
removeKing: false,
shrinkServant: false,
},
})),
},
{
input: "خندل مې",
output: getPeople(1, "sing").map<T.VPSelectionComplete>((person) => ({
blocks: [
{
key: 1,
block: makeSubjectSelectionComplete({
type: "NP",
selection: makePronounSelection(person),
}),
},
{
key: 2,
block: {
type: "objectSelection",
selection: T.Person.ThirdPlurMale,
},
},
],
verb: {
type: "verb",
verb: khandul,
transitivity: "grammatically transitive",
canChangeTransitivity: false,
canChangeStatDyn: false,
negative: false,
tense: "imperfectivePast",
canChangeVoice: true,
isCompound: false,
voice: "active",
},
externalComplement: undefined,
form: {
removeKing: false,
shrinkServant: true,
},
})),
},
{
input: "خندل",
output: [],
},
{
input: "خاندم مې",
output: [],
error: true,
},
{
input: "زه وینم",
output: getPeople(1, "sing").map<T.VPSelectionComplete>((person) => ({
blocks: [
{
key: 1,
block: makeSubjectSelectionComplete({
type: "NP",
selection: makePronounSelection(person),
}),
},
{
key: 2,
block: {
type: "objectSelection",
selection: T.Person.ThirdPlurMale,
},
},
],
verb: {
type: "verb",
verb: leedul,
transitivity: "grammatically transitive",
canChangeTransitivity: false,
canChangeStatDyn: false,
negative: false,
tense: "presentVerb",
canChangeVoice: true,
isCompound: false,
voice: "active",
},
externalComplement: undefined,
form: {
removeKing: false,
shrinkServant: false,
},
})),
},
{
input: "ما ولیدل",
output: getPeople(1, "sing").flatMap<T.VPSelectionComplete>((person) =>
(
["transitive", "grammatically transitive"] as const
).map<T.VPSelectionComplete>((transitivity) => ({
blocks: [
{
key: 1,
block: makeSubjectSelectionComplete({
type: "NP",
selection: makePronounSelection(person),
}),
},
{
key: 2,
block:
transitivity === "grammatically transitive"
? {
type: "objectSelection",
selection: T.Person.ThirdPlurMale,
}
: makeObjectSelectionComplete({
type: "NP",
selection: makePronounSelection(T.Person.ThirdPlurMale),
}),
},
],
verb: {
type: "verb",
verb: leedul,
transitivity,
canChangeTransitivity: false,
canChangeStatDyn: false,
negative: false,
tense: "perfectivePast",
canChangeVoice: true,
isCompound: false,
voice: "active",
},
externalComplement: undefined,
form: {
removeKing: transitivity === "transitive",
shrinkServant: false,
},
}))
),
},
],
},
]; ];
tests.forEach(({ label, cases }) => { tests.forEach(({ label, cases }) => {
@ -1394,6 +1652,7 @@ tests.forEach(({ label, cases }) => {
expect(parsed.map((p) => removeKeys(p.body))).toIncludeSameMembers( expect(parsed.map((p) => removeKeys(p.body))).toIncludeSameMembers(
removeKeys(output) removeKeys(output)
); );
expect(parsed.every((p) => p.errors.length === 0)).toBe(true);
} }
}); });
}); });

View File

@ -55,6 +55,7 @@ export function parseVP(
if (!verb || verb.type !== "VB" || verb.info.type !== "verb") { if (!verb || verb.type !== "VB" || verb.info.type !== "verb") {
return []; return [];
} }
// TODO: check for either VBE or Equative and VBP
if ( if (
!negativeInPlace({ !negativeInPlace({
neg: negIndex, neg: negIndex,
@ -67,7 +68,6 @@ export function parseVP(
return []; return [];
} }
if (verb.info.aspect === "perfective") { if (verb.info.aspect === "perfective") {
// TODO: check that the perfective head is in the right place and actually matches
if (!ph) { if (!ph) {
return []; return [];
} }
@ -77,11 +77,44 @@ export function parseVP(
} }
} }
const tense = getTenseFromRootsStems(ba, verb.info.base, verb.info.aspect); const tense = getTenseFromRootsStems(ba, verb.info.base, verb.info.aspect);
const isPast = isPastTense(tense);
const transitivities = getTransitivities(verb.info.verb); const transitivities = getTransitivities(verb.info.verb);
const results: T.ParseResult<T.VPSelectionComplete>[] = []; const nps = blocks.filter((x): x is T.ParsedNP => x.type === "NP");
// eww... pretty imperative way of doing this... const miniPronouns = getMiniPronouns(kids);
for (let transitivity of transitivities) { return finishPossibleVPSs({
tense,
transitivities,
nps,
miniPronouns,
tokens,
negative,
verb,
});
});
}
function finishPossibleVPSs({
tense,
transitivities,
nps,
miniPronouns,
negative,
verb,
tokens,
}: {
tense: T.VerbTense;
transitivities: T.Transitivity[];
nps: T.ParsedNP[];
miniPronouns: T.ParsedMiniPronoun[];
tokens: Readonly<T.Token[]>;
negative: boolean;
verb: T.ParsedVBE;
}): T.ParseResult<T.VPSelectionComplete>[] {
const isPast = isPastTense(tense);
return transitivities.flatMap<T.ParseResult<T.VPSelectionComplete>>(
(transitivity): T.ParseResult<T.VPSelectionComplete>[] => {
if (verb.info.type === "equative") {
return [];
}
const v: T.VerbSelectionComplete = { const v: T.VerbSelectionComplete = {
type: "verb", type: "verb",
verb: verb.info.verb, verb: verb.info.verb,
@ -94,17 +127,58 @@ export function parseVP(
isCompound: false, isCompound: false,
voice: "active", voice: "active",
}; };
const nps = blocks.filter((x): x is T.ParsedNP => x.type === "NP");
if (transitivity === "intransitive") { if (transitivity === "intransitive") {
return finishIntransitive({
miniPronouns,
nps,
tokens,
v,
verbPerson: verb.person,
});
} else if (transitivity === "transitive") {
return finishTransitive({
miniPronouns,
nps,
tokens,
v,
verbPerson: verb.person,
isPast,
});
} else {
return finishGrammaticallyTransitive({
miniPronouns,
nps,
tokens,
v,
verbPerson: verb.person,
isPast,
});
}
}
);
}
function finishIntransitive({
miniPronouns,
nps,
tokens,
v,
verbPerson,
}: {
miniPronouns: T.ParsedMiniPronoun[];
nps: T.ParsedNP[];
tokens: Readonly<T.Token[]>;
v: T.VerbSelectionComplete;
verbPerson: T.Person;
}): T.ParseResult<T.VPSelectionComplete>[] {
const errors: T.ParseError[] = []; const errors: T.ParseError[] = [];
if (getMiniPronouns(kids).length) { if (miniPronouns.length) {
errors.push({ errors.push({
message: "unknown mini-pronoun", message: "unknown mini-pronoun",
}); });
} }
if (nps.length > 1) { if (nps.length > 1) {
continue; return [];
} }
if (nps.length === 0) { if (nps.length === 0) {
const blocks: T.VPSBlockComplete[] = [ const blocks: T.VPSBlockComplete[] = [
@ -112,7 +186,7 @@ export function parseVP(
key: 1, key: 1,
block: makeSubjectSelectionComplete({ block: makeSubjectSelectionComplete({
type: "NP", type: "NP",
selection: makePronounSelection(verb.person), selection: makePronounSelection(verbPerson),
}), }),
}, },
{ {
@ -123,7 +197,8 @@ export function parseVP(
}, },
}, },
]; ];
results.push({ return [
{
tokens, tokens,
body: { body: {
blocks, blocks,
@ -135,10 +210,10 @@ export function parseVP(
}, },
} as T.VPSelectionComplete, } as T.VPSelectionComplete,
errors, errors,
}); },
continue; ];
} }
if (getPersonFromNP(nps[0].selection) !== verb.person) { if (getPersonFromNP(nps[0].selection) !== verbPerson) {
errors.push({ message: "subject must agree with intransitive verb" }); errors.push({ message: "subject must agree with intransitive verb" });
} }
if (nps[0].inflected) { if (nps[0].inflected) {
@ -159,7 +234,8 @@ export function parseVP(
}, },
}, },
]; ];
results.push({ return [
{
tokens, tokens,
body: { body: {
blocks, blocks,
@ -171,12 +247,28 @@ export function parseVP(
}, },
} as T.VPSelectionComplete, } as T.VPSelectionComplete,
errors, errors,
}); },
continue; ];
} else if (transitivity === "transitive") { }
function finishTransitive({
miniPronouns,
nps,
tokens,
v,
verbPerson,
isPast,
}: {
miniPronouns: T.ParsedMiniPronoun[];
nps: T.ParsedNP[];
tokens: Readonly<T.Token[]>;
v: T.VerbSelectionComplete;
verbPerson: T.Person;
isPast: boolean;
}): T.ParseResult<T.VPSelectionComplete>[] {
// transitive // transitive
if (nps.length > 2) { if (nps.length > 2) {
continue; return [];
} }
if (nps.length === 0) { if (nps.length === 0) {
// present: // present:
@ -186,7 +278,6 @@ export function parseVP(
// - no king (object) // - no king (object)
// - servant (subject) is shrunken // - servant (subject) is shrunken
const errors: T.ParseError[] = []; const errors: T.ParseError[] = [];
const miniPronouns = getMiniPronouns(kids);
if (miniPronouns.length > 1) { if (miniPronouns.length > 1) {
errors.push({ errors.push({
message: "unknown mini-pronoun in kid's section", message: "unknown mini-pronoun in kid's section",
@ -201,7 +292,7 @@ export function parseVP(
key: 1, key: 1,
block: makeSubjectSelectionComplete({ block: makeSubjectSelectionComplete({
type: "NP", type: "NP",
selection: makePronounSelection(verb.person), selection: makePronounSelection(verbPerson),
}), }),
}, },
{ {
@ -224,12 +315,12 @@ export function parseVP(
key: 2, key: 2,
block: makeObjectSelectionComplete({ block: makeObjectSelectionComplete({
type: "NP", type: "NP",
selection: makePronounSelection(verb.person), selection: makePronounSelection(verbPerson),
}), }),
}, },
] ]
); );
const toAdd = blockOpts.flatMap((blocks) => return blockOpts.flatMap((blocks) =>
returnParseResult( returnParseResult(
tokens, tokens,
{ {
@ -246,8 +337,6 @@ export function parseVP(
: errors : errors
) )
); );
toAdd.forEach((r) => results.push(r));
continue;
} }
if (nps.length === 1) { if (nps.length === 1) {
const np = nps[0]; const np = nps[0];
@ -258,7 +347,7 @@ export function parseVP(
// past: // past:
// - no king (np is servant) // - no king (np is servant)
// - shrunken servant (np is king) // - shrunken servant (np is king)
const res = ( return (
[ [
{ {
removeKing: true, removeKing: true,
@ -269,23 +358,23 @@ export function parseVP(
shrinkServant: true, shrinkServant: true,
}, },
] as const ] as const
).flatMap((form) => { ).flatMap<T.ParseResult<T.VPSelectionComplete>>((form) => {
const errors: T.ParseError[] = []; const errors: T.ParseError[] = [];
const king: T.NPSelection = form.removeKing const king: T.NPSelection = form.removeKing
? { ? {
type: "NP", type: "NP",
selection: makePronounSelection(verb.person), selection: makePronounSelection(verbPerson),
} }
: np.selection; : np.selection;
const servants: T.NPSelection[] = form.shrinkServant const servants: T.NPSelection[] = form.shrinkServant
? getPeopleFromMiniPronouns(kids).map((person) => ({ ? getPeopleFromMiniPronouns(miniPronouns).map((person) => ({
type: "NP", type: "NP",
selection: makePronounSelection(person), selection: makePronounSelection(person),
})) }))
: [np.selection]; : [np.selection];
// check for vp structure errors // check for vp structure errors
if (form.removeKing) { if (form.removeKing) {
if (getMiniPronouns(kids).length) { if (miniPronouns.length) {
errors.push({ errors.push({
message: "unknown mini-pronoun in kid's section", message: "unknown mini-pronoun in kid's section",
}); });
@ -314,7 +403,7 @@ export function parseVP(
: "subject of a non-past tense transitive verb should not be inflected", : "subject of a non-past tense transitive verb should not be inflected",
}); });
} }
if (getPersonFromNP(king) !== verb.person) { if (getPersonFromNP(king) !== verbPerson) {
errors.push({ errors.push({
message: `${ message: `${
isPast ? "past tense" : "non-past tense" isPast ? "past tense" : "non-past tense"
@ -360,11 +449,9 @@ export function parseVP(
: errors, : errors,
})); }));
}); });
res.forEach((r) => results.push(r));
continue;
} else { } else {
if (isPast) { if (isPast) {
const res = ( return (
[ [
[nps[0], nps[1], false], [nps[0], nps[1], false],
[nps[1], nps[0], true], [nps[1], nps[0], true],
@ -383,8 +470,7 @@ export function parseVP(
} }
if (!s.inflected) { if (!s.inflected) {
errors.push({ errors.push({
message: message: "subject of transitive past tense verb must be inflected",
"subject of transitive past tense verb must be inflected",
}); });
} }
if (o.inflected) { if (o.inflected) {
@ -393,10 +479,9 @@ export function parseVP(
"object of past tense transitive verb must not be inflected", "object of past tense transitive verb must not be inflected",
}); });
} }
if (getPersonFromNP(o.selection) !== verb.person) { if (getPersonFromNP(o.selection) !== verbPerson) {
errors.push({ errors.push({
message: message: "past tense transitive verb must agree with the object",
"past tense transitive verb must agree with the object",
}); });
} }
let blocks: T.VPSBlockComplete[] = [ let blocks: T.VPSBlockComplete[] = [
@ -426,10 +511,8 @@ export function parseVP(
errors errors
); );
}); });
res.forEach((r) => results.push(r));
continue;
} else { } else {
const res = ( return (
[ [
[nps[0], nps[1], false], [nps[0], nps[1], false],
[nps[1], nps[0], true], [nps[1], nps[0], true],
@ -467,7 +550,7 @@ export function parseVP(
"subject of transitive non-past tense verb must not be inflected", "subject of transitive non-past tense verb must not be inflected",
}); });
} }
if (getPersonFromNP(s.selection) !== verb.person) { if (getPersonFromNP(s.selection) !== verbPerson) {
errors.push({ errors.push({
message: message:
"non-past tense transitive verb must agree with the subject", "non-past tense transitive verb must agree with the subject",
@ -500,29 +583,43 @@ export function parseVP(
errors errors
); );
}); });
res.forEach((r) => results.push(r));
continue;
} }
} }
} else { }
// grammatically transitive
function finishGrammaticallyTransitive({
miniPronouns,
nps,
tokens,
v,
verbPerson,
isPast,
}: {
miniPronouns: T.ParsedMiniPronoun[];
nps: T.ParsedNP[];
tokens: Readonly<T.Token[]>;
v: T.VerbSelectionComplete;
verbPerson: T.Person;
isPast: boolean;
}): T.ParseResult<T.VPSelectionComplete>[] {
const errors: T.ParseError[] = []; const errors: T.ParseError[] = [];
if (isPast) {
if (nps.length === 1) { if (nps.length === 1) {
if (getMiniPronouns(kids).length) { if (miniPronouns.length) {
errors.push({ errors.push({
message: "unknown mini-pronoun", message: "unknown mini-pronoun",
}); });
} }
if (verb.person !== T.Person.ThirdPlurMale) { if (verbPerson !== T.Person.ThirdPlurMale) {
errors.push({ errors.push({
message: message:
"grammatically transitive verb must be 3rd pers. masc. plur.", "grammatically transitive verb must be 3rd pers. masc. plur.",
}); });
} }
if (!nps[0].inflected) { if (isPast && !nps[0].inflected) {
errors.push({ errors.push({
message: message:
"subject of grammatically transitive verb must be inflected", "subject of past tense grammatically transitive verb must be inflected",
}); });
} }
const blocks: T.VPSBlockComplete[] = [ const blocks: T.VPSBlockComplete[] = [
@ -538,7 +635,8 @@ export function parseVP(
}, },
}, },
]; ];
results.push({ return [
{
tokens, tokens,
body: { body: {
blocks, blocks,
@ -550,10 +648,9 @@ export function parseVP(
}, },
} as T.VPSelectionComplete, } as T.VPSelectionComplete,
errors, errors,
}); },
continue; ];
} else if (nps.length === 0) { } else if (nps.length === 0) {
const miniPronouns = getMiniPronouns(kids);
if (miniPronouns.length > 1) { if (miniPronouns.length > 1) {
errors.push({ errors.push({
message: "unknown mini-pronoun", message: "unknown mini-pronoun",
@ -564,13 +661,13 @@ export function parseVP(
message: "subject required for grammatically transitive verb", message: "subject required for grammatically transitive verb",
}); });
} }
if (verb.person !== T.Person.ThirdPlurMale) { if (verbPerson !== T.Person.ThirdPlurMale) {
errors.push({ errors.push({
message: message:
"grammatically transitive verb must be 3rd pers. masc. plur.", "grammatically transitive verb must be 3rd pers. masc. plur.",
}); });
} }
getPeopleFromMiniPronouns(kids).forEach((person) => { return getPeopleFromMiniPronouns(miniPronouns).map((person) => {
const blocks: T.VPSBlockComplete[] = [ const blocks: T.VPSBlockComplete[] = [
{ {
key: 1, key: 1,
@ -587,7 +684,7 @@ export function parseVP(
}, },
}, },
]; ];
results.push({ return {
tokens, tokens,
body: { body: {
blocks, blocks,
@ -599,17 +696,98 @@ export function parseVP(
}, },
} as T.VPSelectionComplete, } as T.VPSelectionComplete,
errors, errors,
};
}); });
}); }
continue;
} else { } else {
continue; // non-past
} if (miniPronouns.length) {
} errors.push({
} message: "unknown mini-pronoun",
return results;
}); });
} }
if (nps.length === 1) {
const subj = nps[0];
if (verbPerson !== getPersonFromNP(subj.selection)) {
errors.push({
message: "non-past verb must agree with subject",
});
}
if (nps[0].inflected) {
errors.push({
message:
"subject of non-past tense grammatically transitive verb must not be inflected",
});
}
const blocks: T.VPSBlockComplete[] = [
{
key: 1,
block: makeSubjectSelectionComplete(nps[0].selection),
},
{
key: 2,
block: {
type: "objectSelection",
selection: T.Person.ThirdPlurMale,
},
},
];
return [
{
tokens,
body: {
blocks,
verb: v,
externalComplement: undefined,
form: {
removeKing: false,
shrinkServant: false,
},
} as T.VPSelectionComplete,
errors,
},
];
} else if (nps.length === 0) {
if (miniPronouns.length > 1) {
errors.push({
message: "unknown mini-pronoun",
});
}
const blocks: T.VPSBlockComplete[] = [
{
key: 1,
block: makeSubjectSelectionComplete({
type: "NP",
selection: makePronounSelection(verbPerson),
}),
},
{
key: 2,
block: {
type: "objectSelection",
selection: T.Person.ThirdPlurMale,
},
},
];
return [
{
tokens,
body: {
blocks,
verb: v,
externalComplement: undefined,
form: {
removeKing: true,
shrinkServant: false,
},
} as T.VPSelectionComplete,
errors,
},
];
}
}
return [];
}
function getMiniPronouns(kids: T.ParsedKid[]): T.ParsedMiniPronoun[] { function getMiniPronouns(kids: T.ParsedKid[]): T.ParsedMiniPronoun[] {
return kids.filter((k): k is T.ParsedMiniPronoun => return kids.filter((k): k is T.ParsedMiniPronoun =>

View File

@ -726,9 +726,7 @@ export type EquativeTenseWithoutBa =
| "subjunctive" | "subjunctive"
| "habitual" | "habitual"
| "past" | "past"
| "wouldBe" | "pastSubjunctive";
| "pastSubjunctive"
| "wouldHaveBeen";
export type PerfectTense = `${EquativeTense}Perfect`; export type PerfectTense = `${EquativeTense}Perfect`;
export type AbilityTense = `${VerbTense}Modal`; export type AbilityTense = `${VerbTense}Modal`;
export type ImperativeTense = `${Aspect}Imperative`; export type ImperativeTense = `${Aspect}Imperative`;
@ -1196,7 +1194,12 @@ export type Block = {
| VHead; | VHead;
}; };
export type ParsedBlock = ParsedNP | ParsedPH | ParsedVBE | NegativeBlock; export type ParsedBlock =
| ParsedNP
| ParsedPH
| ParsedVBE
| ParsedVBP
| NegativeBlock;
export type ParsedKidsSection = { export type ParsedKidsSection = {
type: "kids"; type: "kids";
@ -1213,6 +1216,7 @@ export type ParsedPH = {
s: string; s: string;
}; };
export type ParsedVBE = Omit<VBE, "ps">; export type ParsedVBE = Omit<VBE, "ps">;
export type ParsedVBP = Omit<VBP, "ps">;
export type Kid = { export type Kid = {
key: number; key: number;