Skip to content

Commit 27b20ae

Browse files
committed
error on duplicate definitions
1 parent 7c39327 commit 27b20ae

File tree

2 files changed

+39
-12
lines changed

2 files changed

+39
-12
lines changed

dictionary/parser.ts

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
Parser,
2121
sequence,
2222
UnexpectedError,
23+
UnrecognizedError,
2324
withPosition,
2425
withSource,
2526
} from "../src/parser/parser_lib.ts";
@@ -53,7 +54,9 @@ const ignore = allWithCheck(
5354
function lex<T>(parser: Parser<T>): Parser<T> {
5455
return parser.skip(ignore);
5556
}
56-
const tokiPonaWord = lex(match(/[a-z][a-zA-Z]*/, "word"));
57+
const wordWithPosition = lex(
58+
withPosition(match(/[a-z][a-zA-Z]*/, "Toki Pona word")),
59+
);
5760
const openParenthesis = lex(matchString("(", "open parenthesis"));
5861
const closeParenthesis = lex(matchString(")", "close parenthesis"));
5962
const openBracket = lex(matchString("[", "open bracket"));
@@ -577,7 +580,10 @@ const definition = choiceWithCheck<Definition>(
577580
twoFormPersonalPronounDefinition,
578581
fourFormPersonalPronounDefinition,
579582
);
580-
const head = sequence(all(tokiPonaWord.skip(comma)), tokiPonaWord)
583+
const positionedHead = sequence(
584+
all(wordWithPosition.skip(comma)),
585+
wordWithPosition,
586+
)
581587
.skip(colon)
582588
.map(([init, last]) => [...init, last]);
583589
const entry = withSource(
@@ -592,11 +598,32 @@ const entry = withSource(
592598
)
593599
.map(([definitions, source]) => ({ definitions, source: source.trimEnd() }));
594600
export const dictionaryParser = ignore
595-
.with(allWithCheck(new CheckedParser(notEnd, sequence(head, entry))))
596-
.map((entries) =>
597-
new Map(
598-
entries.flatMap(([words, definition]) =>
599-
words.map((word) => [word, definition])
600-
),
601-
)
602-
);
601+
.with(
602+
allWithCheck(new CheckedParser(notEnd, sequence(positionedHead, entry))),
603+
)
604+
.map((allEntries) => {
605+
const entries = allEntries.flatMap(([words, definition]) =>
606+
words.map((word) => [word, definition] as const)
607+
);
608+
const recorded: Set<string> = new Set();
609+
const errors: Array<UnrecognizedError> = [];
610+
for (const [head] of entries) {
611+
if (recorded.has(head.value)) {
612+
errors.push(
613+
new UnrecognizedError(
614+
`duplicate Toki Pona word "${head.value}"`,
615+
head,
616+
),
617+
);
618+
} else {
619+
recorded.add(head.value);
620+
}
621+
}
622+
if (errors.length > 0) {
623+
throw new AggregateError(errors);
624+
} else {
625+
return new Map(
626+
entries.map(([head, definition]) => [head.value, definition]),
627+
);
628+
}
629+
});

src/parser/parser_lib.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ export class UnexpectedError extends PositionedError {
110110
}
111111
}
112112
export class UnrecognizedError extends PositionedError {
113-
constructor(element: string) {
114-
super(`${element} is unrecognized`);
113+
constructor(element: string, position?: Position) {
114+
super(`${element} is unrecognized`, position);
115115
this.name = "UnrecognizedError";
116116
}
117117
}

0 commit comments

Comments
 (0)