Skip to content

Commit 3a2260c

Browse files
committed
refactor
1 parent 6c9d5e4 commit 3a2260c

27 files changed

+503
-410
lines changed

dictionary/parser.ts

Lines changed: 113 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,19 @@ import {
2424
withPosition,
2525
withSource,
2626
} from "../src/parser/parser_lib.ts";
27-
import { Definition, Dictionary, Noun, PartialVerb } from "./type.ts";
27+
import {
28+
Adjective,
29+
Adverb,
30+
Definition,
31+
Determiner,
32+
Dictionary,
33+
Entry,
34+
IndirectObject,
35+
Noun,
36+
NounForms,
37+
PartialVerb,
38+
PostAdjective,
39+
} from "./type.ts";
2840

2941
const RESERVED_SYMBOLS = "#()*+/:;<=>@[\\]^`{|}~";
3042

@@ -51,7 +63,7 @@ const ignore = allWithCheck(
5163
choiceWithCheck(spaces, comment),
5264
),
5365
);
54-
function lex<const T>(parser: Parser<T>) {
66+
function lex<T>(parser: Parser<T>) {
5567
return parser.skip(ignore);
5668
}
5769
const wordWithPosition = lex(
@@ -65,7 +77,7 @@ const comma = lex(matchString(",", "comma"));
6577
const semicolon = lex(matchString(";", "semicolon"));
6678
const slash = lex(matchString("/", "slash"));
6779

68-
const keyword = memoize(<const T extends string>(keyword: T) =>
80+
const keyword = memoize(<T extends string>(keyword: T) =>
6981
lex(withPosition(match(/[a-z\-]+/, `"${keyword}"`)))
7082
.map((positioned) =>
7183
positioned.value === keyword ? positioned.value : throwError(
@@ -100,10 +112,10 @@ const perspective = choiceOnlyOne(
100112
keyword("second"),
101113
keyword("third"),
102114
);
103-
function tag<const T>(parser: Parser<T>) {
115+
function tag<T>(parser: Parser<T>) {
104116
return openParenthesis.with(parser).skip(closeParenthesis);
105117
}
106-
function template<const T>(parser: Parser<T>) {
118+
function template<T>(parser: Parser<T>) {
107119
return openBracket.with(parser).skip(closeBracket);
108120
}
109121
const simpleUnit = memoize((kind: string) => word.skip(tag(keyword(kind))));
@@ -120,60 +132,64 @@ const nounOnly = checkedSequence(
120132
sequence(optionalAll(keyword("gerund")), optionalNumber)
121133
.skip(closeParenthesis),
122134
)
123-
.mapWithPositionedError(([[noun, plural], [gerund, number]]) => {
124-
if (plural == null) {
125-
if (number == null) {
126-
const sentence = nlp(noun);
127-
sentence.tag("Noun");
128-
const singular = sentence
129-
.nouns()
130-
.toSingular()
131-
.text();
132-
const plural = sentence
133-
.nouns()
134-
.toPlural()
135-
.text();
136-
if (singular === "" || plural === "") {
137-
throw `no singular or plural form found for "${noun}". consider ` +
138-
"providing both singular and plural forms instead";
135+
.mapWithPositionedError(
136+
(
137+
[[noun, plural], [gerund, number]],
138+
): NounForms & Readonly<{ gerund: boolean }> => {
139+
if (plural == null) {
140+
if (number == null) {
141+
const sentence = nlp(noun);
142+
sentence.tag("Noun");
143+
const singular = sentence
144+
.nouns()
145+
.toSingular()
146+
.text();
147+
const plural = sentence
148+
.nouns()
149+
.toPlural()
150+
.text();
151+
if (singular === "" || plural === "") {
152+
throw `no singular or plural form found for "${noun}". consider ` +
153+
"providing both singular and plural forms instead";
154+
}
155+
if (noun !== singular) {
156+
throw `conjugation error: "${noun}" is not "${singular}". ` +
157+
"consider providing both singular and plural forms instead";
158+
}
159+
return {
160+
singular: escapeHtml(singular),
161+
plural: escapeHtml(plural),
162+
gerund: gerund != null,
163+
};
164+
} else {
165+
const escaped = escapeHtml(noun);
166+
let singular: null | string;
167+
let plural: null | string;
168+
switch (number) {
169+
case "singular":
170+
singular = escaped;
171+
plural = null;
172+
break;
173+
case "plural":
174+
singular = null;
175+
plural = escaped;
176+
break;
177+
}
178+
return { singular, plural, gerund: gerund != null };
139179
}
140-
if (noun !== singular) {
141-
throw `conjugation error: "${noun}" is not "${singular}". ` +
142-
"consider providing both singular and plural forms instead";
180+
} else {
181+
if (number != null) {
182+
throw "plural or singular keyword within tag " +
183+
"must not be provided when singular and plural forms are defined";
143184
}
144185
return {
145-
singular: escapeHtml(singular),
146-
plural: escapeHtml(plural),
186+
singular: escapeHtml(noun),
187+
plural,
147188
gerund: gerund != null,
148189
};
149-
} else {
150-
const escaped = escapeHtml(noun);
151-
let singular: null | string;
152-
let plural: null | string;
153-
switch (number) {
154-
case "singular":
155-
singular = escaped;
156-
plural = null;
157-
break;
158-
case "plural":
159-
singular = null;
160-
plural = escaped;
161-
break;
162-
}
163-
return { singular, plural, gerund: gerund != null };
164190
}
165-
} else {
166-
if (number != null) {
167-
throw "plural or singular keyword within tag " +
168-
"must not be provided when singular and plural forms are defined";
169-
}
170-
return {
171-
singular: escapeHtml(noun),
172-
plural,
173-
gerund: gerund != null,
174-
};
175-
}
176-
});
191+
},
192+
);
177193
const determinerType = choiceOnlyOne(
178194
keyword("article"),
179195
keyword("demonstrative"),
@@ -193,7 +209,7 @@ const determiner = checkedSequence(
193209
),
194210
sequence(determinerType, optionalNumber.skip(closeParenthesis)),
195211
)
196-
.map(([[determiner, plural], [kind, quantity]]) => ({
212+
.map(([[determiner, plural], [kind, quantity]]): Determiner => ({
197213
determiner,
198214
plural,
199215
kind,
@@ -218,7 +234,7 @@ const adverb = checkedSequence(
218234
word.skip(openParenthesis).skip(keyword("adv")),
219235
optionalAll(keyword("negative")).skip(closeParenthesis),
220236
)
221-
.map(([adverb, negative]) => ({
237+
.map(([adverb, negative]): Adverb => ({
222238
adverb,
223239
negative: negative != null,
224240
}));
@@ -232,7 +248,7 @@ const adjective = checkedSequence(
232248
optionalAll(keyword("gerund-like")).skip(closeParenthesis),
233249
),
234250
)
235-
.map(([[adverbs, adjective], [kind, gerundLike]]) => ({
251+
.map(([[adverbs, adjective], [kind, gerundLike]]): Adjective => ({
236252
adverbs,
237253
adjective,
238254
kind,
@@ -247,10 +263,10 @@ const noun = sequence(
247263
simpleUnit("adj"),
248264
word.skip(tag(sequence(keyword("n"), keyword("proper")))),
249265
)
250-
.map(([adjective, name]) => ({ adjective, name })),
266+
.map(([adjective, name]): PostAdjective => ({ adjective, name })),
251267
),
252268
)
253-
.map(([determiners, adjectives, noun, postAdjective]) => ({
269+
.map(([determiners, adjectives, noun, postAdjective]): Noun => ({
254270
...noun,
255271
determiners,
256272
adjectives,
@@ -264,7 +280,7 @@ const checkedNoun = new CheckedParser(
264280
),
265281
noun,
266282
);
267-
function checkedSimpleUnitWith<const T>(tag: string, after: Parser<T>) {
283+
function checkedSimpleUnitWith<T>(tag: string, after: Parser<T>) {
268284
return checkedSequence(
269285
word.skip(openParenthesis).skip(keyword(tag)),
270286
closeParenthesis.with(after),
@@ -281,19 +297,22 @@ function checkedSimpleUnitWithTemplate(
281297
.map(([word]) => word);
282298
}
283299
const interjectionDefinition = checkedSimpleUnit("i")
284-
.map((interjection) => ({ type: "interjection", interjection }));
300+
.map((interjection): Definition => ({ type: "interjection", interjection }));
285301
const particleDefinition = checkedSequence(
286302
word.skip(openParenthesis).skip(keyword("particle")),
287303
sequence(keyword("def"), closeParenthesis),
288304
)
289-
.map(([definition]) => ({ type: "particle definition", definition }));
305+
.map(([definition]): Definition => ({
306+
type: "particle definition",
307+
definition,
308+
}));
290309
const prepositionDefinition = checkedSimpleUnitWithTemplate(
291310
"prep",
292311
sequence(keyword("indirect"), keyword("object")),
293312
)
294-
.map((preposition) => ({ type: "preposition", preposition }));
313+
.map((preposition): Definition => ({ type: "preposition", preposition }));
295314
const numeralDefinition = checkedSimpleUnit("num")
296-
.mapWithPositionedError((num) => {
315+
.mapWithPositionedError((num): Definition => {
297316
const numeral = +num;
298317
if (!Number.isInteger(numeral) || numeral < 0) {
299318
throw `"${num}" is not a non-negative integer`;
@@ -313,7 +332,7 @@ const fillerDefinition = checkedSequence(
313332
.map(([first, rest]) => [first, ...rest]),
314333
closeParenthesis,
315334
)
316-
.mapWithPositionedError(([forms]) => {
335+
.mapWithPositionedError(([forms]): Definition => {
317336
if (forms.length === 1) {
318337
return {
319338
type: "filler",
@@ -349,7 +368,7 @@ const fourFormPersonalPronounDefinition = checkedSequence(
349368
.map(([
350369
[singularSubject, singularObject, pluralSubject, pluralObject],
351370
perspective,
352-
]) => ({
371+
]): Definition => ({
353372
type: "personal pronoun",
354373
singular: { subject: singularSubject, object: singularObject },
355374
plural: { subject: pluralSubject, object: pluralObject },
@@ -365,7 +384,7 @@ const twoFormPersonalPronounDefinition = checkedSequence(
365384
number.skip(closeParenthesis),
366385
),
367386
)
368-
.map(([[subject, object], [perspective, number]]) => ({
387+
.map(([[subject, object], [perspective, number]]): Definition => ({
369388
type: "personal pronoun",
370389
singular: null,
371390
plural: null,
@@ -397,7 +416,7 @@ const nounDefinition = new CheckedParser(
397416
),
398417
),
399418
)
400-
.map(([noun, preposition]) =>
419+
.map(([noun, preposition]): Definition =>
401420
preposition == null
402421
? { ...noun, type: "noun" }
403422
: { type: "noun preposition", noun, preposition }
@@ -410,7 +429,10 @@ const compoundAdjectiveDefinition = checkedSequence(
410429
.skip(keyword("c")),
411430
closeParenthesis.with(adjective.parser),
412431
)
413-
.map((adjectives) => ({ type: "compound adjective", adjectives }))
432+
.map((adjectives): Definition & { type: "compound adjective" } => ({
433+
type: "compound adjective",
434+
adjectives,
435+
}))
414436
.filterWithPositionedError(({ adjectives }) =>
415437
adjectives.every((adjective) => adjective.adverbs.length === 0) ||
416438
throwError("compound adjective cannot have adverbs")
@@ -430,11 +452,14 @@ const verbDefinition = checkedSequence(
430452
closeBracket
431453
.with(optionalWithCheck(
432454
checkedSimpleUnitWith("prep", noun)
433-
.map(([preposition, object]) => ({ preposition, object })),
455+
.map(([preposition, object]): IndirectObject => ({
456+
preposition,
457+
object,
458+
})),
434459
))
435460
.map(nullableAsArray),
436461
)
437-
.map(([_, indirectObjects]) => ({
462+
.map(([_, indirectObjects]): null | PartialVerb => ({
438463
directObject: null,
439464
indirectObjects,
440465
forObject: true,
@@ -444,7 +469,7 @@ const verbDefinition = checkedSequence(
444469
sequence(closeParenthesis, openBracket, keyword("predicate")),
445470
closeBracket,
446471
)
447-
.map(() => ({
472+
.map((): null | PartialVerb => ({
448473
directObject: null,
449474
indirectObjects: [],
450475
forObject: false,
@@ -454,12 +479,12 @@ const verbDefinition = checkedSequence(
454479
keyword("modal"),
455480
sequence(closeParenthesis, template(keyword("predicate"))),
456481
)
457-
.map(() => null),
482+
.map((): null | PartialVerb => null),
458483
checkedSequence(
459484
keyword("linking"),
460485
sequence(closeParenthesis, template(keyword("predicate"))),
461486
)
462-
.map(() => ({
487+
.map((): null | PartialVerb => ({
463488
directObject: null,
464489
indirectObjects: [],
465490
forObject: false,
@@ -484,7 +509,7 @@ const verbDefinition = checkedSequence(
484509
),
485510
),
486511
)
487-
.map<PartialVerb>(([_, [directObject, rawIndirectObject]]) => {
512+
.map(([_, [directObject, rawIndirectObject]]): PartialVerb => {
488513
if (rawIndirectObject == null) {
489514
return {
490515
directObject,
@@ -516,7 +541,7 @@ const verbDefinition = checkedSequence(
516541
}),
517542
),
518543
)
519-
.mapWithPositionedError<Definition>(([[verb, forms], rest]) => {
544+
.mapWithPositionedError(([[verb, forms], rest]): Definition => {
520545
if (rest == null) {
521546
if (forms != null) {
522547
throw "modal verbs shouldn't be conjugated";
@@ -561,12 +586,18 @@ const definition = choiceWithCheck(
561586
// compound adjective parser must come before adjective parser
562587
compoundAdjectiveDefinition,
563588
// adjective parser must come before adverb parser
564-
adjective.map((adjective) => ({ ...adjective, type: "adjective" })),
589+
adjective.map((adjective): Definition => ({
590+
...adjective,
591+
type: "adjective",
592+
})),
565593
verbDefinition,
566-
adverb.map((adverb) => ({ ...adverb, type: "adverb" })),
594+
adverb.map((adverb): Definition => ({ ...adverb, type: "adverb" })),
567595
interjectionDefinition,
568596
particleDefinition,
569-
determiner.map((determiner) => ({ ...determiner, type: "determiner" })),
597+
determiner.map((determiner): Definition => ({
598+
...determiner,
599+
type: "determiner",
600+
})),
570601
prepositionDefinition,
571602
numeralDefinition,
572603
fillerDefinition,
@@ -589,7 +620,10 @@ const entry = withSource(
589620
),
590621
),
591622
)
592-
.map(([definitions, source]) => ({ definitions, source: source.trimEnd() }));
623+
.map(([definitions, source]): Entry => ({
624+
definitions,
625+
source: source.trimEnd(),
626+
}));
593627
export const dictionaryParser: Parser<Dictionary> = ignore
594628
.with(
595629
allWithCheck(new CheckedParser(notEnd, sequence(positionedHead, entry))),

0 commit comments

Comments
 (0)