diff --git a/src/demo-components/ParserDemo.tsx b/src/demo-components/ParserDemo.tsx index 81a342f..05785e4 100644 --- a/src/demo-components/ParserDemo.tsx +++ b/src/demo-components/ParserDemo.tsx @@ -17,6 +17,7 @@ const working = [ "noun phrases (except participles)", "mini-pronouns for shrunken servants", "grammar error correction", + "negatives", ]; const todo = [ @@ -42,6 +43,7 @@ const examples = [ "د غټې ماشومې زاړه پلار ولیدم", "ستا پخواني ملګري مې ولیدل", "ما ډوډۍ خوړله", + "وامې نه خیست", ]; function ParserDemo({ opts }: { opts: T.TextOptions }) { diff --git a/src/lib/src/parsing/parse-blocks.ts b/src/lib/src/parsing/parse-blocks.ts index fd1515d..f4a6f99 100644 --- a/src/lib/src/parsing/parse-blocks.ts +++ b/src/lib/src/parsing/parse-blocks.ts @@ -1,6 +1,7 @@ import * as T from "../../../types"; import { fmapParseResult } from "../fp-ps"; import { parseKidsSection } from "./parse-kids-section"; +import { parseNeg } from "./parse-negative"; import { parseNP } from "./parse-np"; import { parsePH } from "./parse-ph"; import { parseVerb } from "./parse-verb"; @@ -31,8 +32,9 @@ export function parseBlocks( ([ph, v]) => (ph ? [ph, v] : [v]), parseVerb(tokens, verbLookup) ); + const neg = fmapParseResult((x) => [x], parseNeg(tokens)); const kidsR = parseKidsSection(tokens, []); - const allResults = [...np, ...ph, ...vb, ...kidsR] as T.ParseResult< + const allResults = [...np, ...ph, ...neg, ...vb, ...kidsR] as T.ParseResult< T.ParsedBlock[] | { kids: T.ParsedKid[] } >[]; // TODO: is this necessary? @@ -46,6 +48,7 @@ export function parseBlocks( // ]; // } return bindParseResult(allResults, (tokens, r) => { + const errors: T.ParseError[] = []; if ("kids" in r) { return { next: parseBlocks(tokens, lookup, verbLookup, blocks, [ @@ -65,7 +68,18 @@ export function parseBlocks( if (!phMatches(prevPh, vb)) { return []; } - return parseBlocks(tokens, lookup, verbLookup, [...blocks, ...r], kids); + // don't allow two negatives + if ( + "type" in r[0] && + r[0].type === "negative" && + blocks.some((b) => "type" in b && b.type === "negative") + ) { + return []; + } + return { + next: parseBlocks(tokens, lookup, verbLookup, [...blocks, ...r], kids), + errors, + }; }); } diff --git a/src/lib/src/parsing/parse-negative.ts b/src/lib/src/parsing/parse-negative.ts new file mode 100644 index 0000000..0eec41f --- /dev/null +++ b/src/lib/src/parsing/parse-negative.ts @@ -0,0 +1,24 @@ +import * as T from "../../../types"; +import { returnParseResult } from "./utils"; + +export function parseNeg( + tokens: Readonly +): T.ParseResult[] { + if (tokens.length === 0) { + return []; + } + const [{ s }, ...rest] = tokens; + if (s === "نه") { + return returnParseResult(rest, { + type: "negative", + imperative: false, + }); + } + if (s === "مه") { + return returnParseResult(rest, { + type: "negative", + imperative: true, + }); + } + return []; +} diff --git a/src/lib/src/parsing/parse-vp.ts b/src/lib/src/parsing/parse-vp.ts index 222fee0..ca6aead 100644 --- a/src/lib/src/parsing/parse-vp.ts +++ b/src/lib/src/parsing/parse-vp.ts @@ -36,6 +36,8 @@ import { isFirstOrSecondPersPronoun } from "../phrase-building/render-vp"; // TODO: transitivity options +// TODO: the و is really making it slow down... why? + export function parseVP( tokens: Readonly, lookup: (s: Partial) => T.DictionaryEntry[], @@ -46,16 +48,30 @@ export function parseVP( } const blocks = parseBlocks(tokens, lookup, verbLookup, [], []); return bindParseResult(blocks, (tokens, { blocks, kids }) => { - const ph = blocks.find((x) => "type" in x && x.type === "PH") as - | T.ParsedPH - | undefined; - const verb = blocks.find((x) => "type" in x && x.type === "VB") as - | T.ParsedVBE - | undefined; + const phIndex = blocks.findIndex((x) => "type" in x && x.type === "PH"); + const vbeIndex = blocks.findIndex((x) => "type" in x && x.type === "VB"); const ba = !!kids.find((k) => k === "ba"); + const negIndex = blocks.findIndex( + (x) => "type" in x && x.type === "negative" && !x.imperative + ); + const ph = phIndex !== -1 ? (blocks[phIndex] as T.ParsedPH) : undefined; + const verb = + vbeIndex !== -1 ? (blocks[vbeIndex] as T.ParsedVBE) : undefined; + const negative = negIndex !== -1; if (!verb || verb.type !== "VB" || verb.info.type !== "verb") { return []; } + if ( + !negativeInPlace({ + neg: negIndex, + v: vbeIndex, + phIndex: phIndex, + ph, + kids: !!kids.length, + }) + ) { + return []; + } if (verb.info.aspect === "perfective") { // TODO: check that the perfective head is in the right place and actually matches if (!ph) { @@ -77,7 +93,7 @@ export function parseVP( : "transitive", canChangeTransitivity: false, canChangeStatDyn: false, - negative: false, + negative, tense, canChangeVoice: true, isCompound: false, @@ -509,3 +525,31 @@ function getTenseFromRootsStems( } } } + +function negativeInPlace({ + neg, + v, + phIndex, + ph, + kids, +}: { + neg: number; + v: number; + phIndex: number; + ph: T.ParsedPH | undefined; + kids: boolean; +}): boolean { + if (neg === -1) { + return true; + } + if (ph) { + if (!kids && !["و", "وا"].includes(ph.s) && neg === phIndex - 1) { + return true; + } + return neg === phIndex + 1; + } + if (neg < v - 1) { + return false; + } + return true; +} diff --git a/src/types.ts b/src/types.ts index f43ccf8..d21f194 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1176,6 +1176,8 @@ export type EntryLookupPortal = { export type EquativeBlock = { type: "equative"; equative: EquativeRendered }; +export type NegativeBlock = { type: "negative"; imperative: boolean }; + export type Block = { key: number; block: @@ -1185,14 +1187,14 @@ export type Block = { | Rendered | Rendered | Rendered - | { type: "negative"; imperative: boolean } + | NegativeBlock | EquativeBlock | VB | VBE | VHead; }; -export type ParsedBlock = ParsedNP | ParsedPH | ParsedVBE; +export type ParsedBlock = ParsedNP | ParsedPH | ParsedVBE | NegativeBlock; export type ParsedNP = { inflected: boolean;