some error messages

This commit is contained in:
adueck 2023-07-31 00:06:21 +04:00
parent c38e0645d3
commit 6aec2dfeb2
6 changed files with 138 additions and 22 deletions

View File

@ -7,16 +7,19 @@ import { tokenizer } from "../lib/src/parsing/tokenizer";
function ParserDemo({ opts }: { opts: T.TextOptions }) { function ParserDemo({ opts }: { opts: T.TextOptions }) {
const [text, setText] = useState<string>(""); const [text, setText] = useState<string>("");
const [result, setResult] = useState<string>(""); const [result, setResult] = useState<string>("");
const [errors, setErrors] = useState<string[]>([]);
function handleChange(e: React.ChangeEvent<HTMLInputElement>) { function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
const value = e.target.value; const value = e.target.value;
if (!value) { if (!value) {
setText(""); setText("");
setResult(""); setResult("");
setErrors([]);
return; return;
} }
const r = parsePhrase(tokenizer(value), lookup); const { success, errors } = parsePhrase(tokenizer(value), lookup);
setText(value); setText(value);
setResult(JSON.stringify(r, null, " ")); setErrors(errors);
setResult(JSON.stringify(success, null, " "));
} }
return ( return (
<div className="mt-3" style={{ marginBottom: "1000px" }}> <div className="mt-3" style={{ marginBottom: "1000px" }}>
@ -32,6 +35,13 @@ function ParserDemo({ opts }: { opts: T.TextOptions }) {
onChange={handleChange} onChange={handleChange}
/> />
</div> </div>
{result === "[]" && errors.length > 0 && (
<div className="alert alert-danger" role="alert">
{errors.map((e) => (
<div key={Math.random()}>{e}</div>
))}
</div>
)}
<samp> <samp>
<pre>{result}</pre> <pre>{result}</pre>
</samp> </samp>

View File

@ -6,6 +6,7 @@ import {
isPattern, isPattern,
isPattern5Entry, isPattern5Entry,
isPattern4Entry, isPattern4Entry,
isPattern6FemEntry,
} from "../type-predicates"; } from "../type-predicates";
import { equals } from "rambda"; import { equals } from "rambda";
@ -135,6 +136,14 @@ export function getInflectionQueries(
predicate: (e) => isPattern2Entry(e) || isPattern3Entry(e), predicate: (e) => isPattern2Entry(e) || isPattern3Entry(e),
}, },
}); });
queries.push({
search: { p: s },
details: {
inflection: [0],
gender: ["fem"],
predicate: isPattern6FemEntry,
},
});
} else if (s.endsWith("و")) { } else if (s.endsWith("و")) {
queries.push({ queries.push({
search: { p: s.slice(0, -1) }, search: { p: s.slice(0, -1) },
@ -179,6 +188,24 @@ export function getInflectionQueries(
predicate: isPattern3Entry, predicate: isPattern3Entry,
}, },
}); });
if (includeNouns) {
queries.push({
search: { p: s.slice(0, -1) + "ي" },
details: {
inflection: [1],
gender: ["fem"],
predicate: isPattern6FemEntry,
},
});
queries.push({
search: { p: s },
details: {
inflection: [0, 1],
gender: ["fem"],
predicate: isPattern3Entry,
},
});
}
} }
const coallated: ReturnType<typeof getInflectionQueries> = []; const coallated: ReturnType<typeof getInflectionQueries> = [];

View File

@ -313,7 +313,12 @@ describe("parsing adjectives", () => {
cases.forEach(({ input, output }) => { cases.forEach(({ input, output }) => {
const tokens = tokenizer(input); const tokens = tokenizer(input);
const possibilities = parseAdjective(tokens, lookup).map((x) => x[1]); const possibilities = parseAdjective(tokens, lookup).map((x) => x[1]);
expect(possibilities).toEqual(output); expect(
possibilities.map((x) => {
const { given, ...rest } = x;
return rest;
})
).toEqual(output);
}); });
}); });
}); });

View File

@ -11,6 +11,7 @@ export function parseAdjective(
{ {
inflection: (0 | 1 | 2)[]; inflection: (0 | 1 | 2)[];
gender: T.Gender[]; gender: T.Gender[];
given: string;
selection: T.AdjectiveSelection; selection: T.AdjectiveSelection;
} }
][] { ][] {
@ -32,6 +33,7 @@ export function parseAdjective(
selection, selection,
inflection: deets.inflection, inflection: deets.inflection,
gender: deets.gender, gender: deets.gender,
given: first,
}, },
]); ]);
}); });

View File

@ -15,17 +15,28 @@ export function parseNoun(
adjectives: { adjectives: {
inflection: (0 | 1 | 2)[]; inflection: (0 | 1 | 2)[];
gender: T.Gender[]; gender: T.Gender[];
given: string;
selection: T.AdjectiveSelection; selection: T.AdjectiveSelection;
}[] }[]
): [string[], { inflection: (0 | 1 | 2)[]; selection: T.NounSelection }][] { ): {
success: [
string[],
{ inflection: (0 | 1 | 2)[]; selection: T.NounSelection }
][];
errors: string[];
} {
if (tokens.length === 0) { if (tokens.length === 0) {
return []; return {
success: [],
errors: [],
};
} }
const adjRes = parseAdjective(tokens, lookup); const adjRes = parseAdjective(tokens, lookup);
const withAdj = adjRes.flatMap(([tkns, adj]) => const withAdj = adjRes.map(([tkns, adj]) =>
parseNoun(tkns, lookup, [...adjectives, adj]) parseNoun(tkns, lookup, [...adjectives, adj])
); );
const w: ReturnType<typeof parseNoun> = []; const success: ReturnType<typeof parseNoun>["success"] = [];
const errors: string[] = [];
const [first, ...rest] = tokens; const [first, ...rest] = tokens;
const searches = getInflectionQueries(first, true); const searches = getInflectionQueries(first, true);
@ -34,11 +45,15 @@ export function parseNoun(
details.forEach((deets) => { details.forEach((deets) => {
const fittingEntries = nounEntries.filter(deets.predicate); const fittingEntries = nounEntries.filter(deets.predicate);
fittingEntries.forEach((entry) => { fittingEntries.forEach((entry) => {
console.log({ entry, deets });
if (isUnisexNounEntry(entry)) { if (isUnisexNounEntry(entry)) {
deets.gender.forEach((gender) => { deets.gender.forEach((gender) => {
if (adjsMatch(adjectives, gender, deets.inflection)) { const { ok, error } = adjsMatch(
w.push([ adjectives,
gender,
deets.inflection
);
if (ok) {
success.push([
rest, rest,
{ {
inflection: deets.inflection, inflection: deets.inflection,
@ -49,11 +64,16 @@ export function parseNoun(
}, },
}, },
]); ]);
} else {
error.forEach((e) => {
errors.push(e);
});
} }
}); });
} else if (isMascNounEntry(entry) && deets.gender.includes("masc")) { } else if (isMascNounEntry(entry) && deets.gender.includes("masc")) {
if (adjsMatch(adjectives, "masc", deets.inflection)) { const { ok, error } = adjsMatch(adjectives, "masc", deets.inflection);
w.push([ if (ok) {
success.push([
rest, rest,
{ {
inflection: deets.inflection, inflection: deets.inflection,
@ -63,10 +83,15 @@ export function parseNoun(
}, },
}, },
]); ]);
} else {
error.forEach((e) => {
errors.push(e);
});
} }
} else if (isFemNounEntry(entry) && deets.gender.includes("fem")) { } else if (isFemNounEntry(entry) && deets.gender.includes("fem")) {
if (adjsMatch(adjectives, "fem", deets.inflection)) { const { ok, error } = adjsMatch(adjectives, "fem", deets.inflection);
w.push([ if (ok) {
success.push([
rest, rest,
{ {
inflection: deets.inflection, inflection: deets.inflection,
@ -76,22 +101,63 @@ export function parseNoun(
}, },
}, },
]); ]);
} else {
error.forEach((e) => {
errors.push(e);
});
} }
} }
}); });
}); });
}); });
return [...withAdj, ...w]; return {
success: [...withAdj.map((x) => x.success).flat(), ...success],
errors: [...withAdj.map((x) => x.errors).flat(), ...errors],
};
} }
function adjsMatch( function adjsMatch(
adjectives: Parameters<typeof parseNoun>[2], adjectives: Parameters<typeof parseNoun>[2],
gender: T.Gender, gender: T.Gender,
inflection: (0 | 1 | 2)[] inflection: (0 | 1 | 2)[]
): boolean { ): { ok: boolean; error: string[] } {
return adjectives.every( const unmatching = adjectives.filter(
(adj) => (adj) =>
adj.gender.includes(gender) && !adj.gender.includes(gender) ||
adj.inflection.some((i) => inflection.includes(i)) !adj.inflection.some((i) => inflection.includes(i))
); );
if (unmatching.length) {
return {
ok: false,
error: unmatching.map((x) => {
const adjText =
x.given === x.selection.entry.p
? x.given
: `${x.given} (${x.selection.entry.p})`;
const inflectionIssue = !x.inflection.some((x) =>
inflection.includes(x)
)
? ` should be ${showInflection(inflection)}`
: ``;
return `Adjective agreement error: ${adjText} should be ${inflectionIssue} ${gender}.`;
}),
};
} else {
return {
ok: true,
error: [],
};
}
}
function showInflection(inf: (0 | 1 | 2)[]): string {
const [last, ...rest] = inf.reverse();
const template = rest.length
? `${rest.join(", ")}, or ${last}`
: last.toString();
console.log(template);
return template
.replace("0", "plain")
.replace("1", "first inflection")
.replace("2", "second inflection");
} }

View File

@ -6,13 +6,19 @@ import { parseNoun } from "./parse-noun";
export function parsePhrase( export function parsePhrase(
s: string[], s: string[],
lookup: (s: Partial<T.DictionaryEntry>) => T.DictionaryEntry[] lookup: (s: Partial<T.DictionaryEntry>) => T.DictionaryEntry[]
): any[] { ): {
success: any[];
errors: string[];
} {
const adjsRes = parseAdjective(s, lookup); const adjsRes = parseAdjective(s, lookup);
const prnsRes = parsePronoun(s); const prnsRes = parsePronoun(s);
const nounsRes = parseNoun(s, lookup, []); const nounsRes = parseNoun(s, lookup, []);
const correct = [...adjsRes, ...prnsRes, ...nounsRes] const correct = [...adjsRes, ...prnsRes, ...nounsRes.success]
.filter(([tkns]) => tkns.length === 0) .filter(([tkns]) => tkns.length === 0)
.map((x) => x[1]); .map((x) => x[1]);
return correct; return {
success: correct,
errors: nounsRes.errors,
};
} }