Skip to content

Commit 80252a1

Browse files
committed
implement various inflection resolver
1 parent 605acd8 commit 80252a1

5 files changed

Lines changed: 181 additions & 8 deletions

File tree

src/resolver_and_composer/ast.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ export type AdjectivePhrase =
4545
type: "compound";
4646
conjunction: string;
4747
adjectives: ReadonlyArray<AdjectivePhrase>;
48-
emphasis: boolean;
4948
}>;
5049
export type Complement =
5150
| Readonly<{ type: "noun"; noun: NounPhrase }>
@@ -60,6 +59,7 @@ export type VerbAdverb = {
6059
verb: Word;
6160
postAdverb: null | Adverb;
6261
};
62+
// TODO: just turn this into an array
6363
export type Verb = Readonly<{
6464
modal: null | VerbAdverb;
6565
verbs: ReadonlyArray<VerbAdverb>;

src/resolver_and_composer/composer.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,23 +39,21 @@ export function adjective(
3939
phrases: English.AdjectivePhrase,
4040
depth: number,
4141
): string {
42-
let text: string;
4342
switch (phrases.type) {
4443
case "simple":
45-
text = [
44+
const text = [
4645
...phrases.adverbs.map(({ adverb }) => word(adverb)),
4746
word(phrases.adjective),
4847
]
4948
.join(" ");
50-
break;
49+
return word({ word: text, emphasis: phrases.emphasis });
5150
case "compound":
52-
text = compound(
51+
return compound(
5352
phrases.adjectives.map((phrase) => adjective(phrase, depth + 1)),
5453
phrases.conjunction,
5554
depth !== 0,
5655
);
5756
}
58-
return word({ word: text, emphasis: phrases.emphasis });
5957
}
6058
function preposition(preposition: English.Preposition) {
6159
return word({
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import { IterableResult } from "../compound.ts";
2+
import * as Dictionary from "../dictionary/type.ts";
3+
import { nullableAsArray } from "../misc/misc.ts";
4+
import { settings } from "../settings.ts";
5+
import * as UnresolvedEnglish from "../translator/ast.ts";
6+
import { FilteredError } from "../translator/error.ts";
7+
import { condense } from "../translator_legacy/misc.ts";
8+
import * as ResolvedEnglish from "./ast.ts";
9+
10+
type Place = "subject" | "object";
11+
12+
type NounQuantity = Readonly<{
13+
noun: string;
14+
quantity: ResolvedEnglish.Quantity;
15+
}>;
16+
function fromNounForms(
17+
nounForms: Dictionary.NounForms,
18+
determinerNumber: Dictionary.Quantity,
19+
): IterableResult<NounQuantity> {
20+
const { singular, plural } = nounForms;
21+
switch (determinerNumber) {
22+
case "singular":
23+
case "plural": {
24+
let noun: null | string;
25+
switch (determinerNumber) {
26+
case "singular":
27+
noun = singular;
28+
break;
29+
case "plural":
30+
noun = plural;
31+
break;
32+
}
33+
return IterableResult.fromNullable(noun)
34+
.map((noun): NounQuantity => ({ noun, quantity: determinerNumber }));
35+
}
36+
case "both":
37+
switch (settings.quantity) {
38+
case "both":
39+
return IterableResult.fromArray([
40+
...nullableAsArray(singular)
41+
.map((noun): NounQuantity => ({ noun, quantity: "singular" })),
42+
...nullableAsArray(plural)
43+
.map((noun): NounQuantity => ({ noun, quantity: "plural" })),
44+
]);
45+
case "condensed":
46+
if (singular != null && plural != null) {
47+
return IterableResult.single<NounQuantity>({
48+
noun: condense(singular, plural),
49+
quantity: "condensed",
50+
});
51+
}
52+
// fallthrough
53+
case "default only":
54+
if (singular != null) {
55+
return IterableResult.single<NounQuantity>({
56+
noun: singular,
57+
quantity: "singular",
58+
});
59+
} else {
60+
return IterableResult.single<NounQuantity>({
61+
noun: plural!,
62+
quantity: "plural",
63+
});
64+
}
65+
}
66+
}
67+
}
68+
function simpleNounForms(
69+
nounForms: Dictionary.NounForms,
70+
): IterableResult<string> {
71+
return fromNounForms(nounForms, "both").map(({ noun }) => noun);
72+
}
73+
function pronounForms(
74+
pronoun: Dictionary.PronounForms,
75+
place: Place,
76+
): Dictionary.NounForms {
77+
switch (place) {
78+
case "subject":
79+
return {
80+
singular: pronoun.singular?.subject ?? null,
81+
plural: pronoun.plural?.subject ?? null,
82+
};
83+
case "object":
84+
return {
85+
singular: pronoun.singular?.object ?? null,
86+
plural: pronoun.plural?.object ?? null,
87+
};
88+
}
89+
}
90+
function check(
91+
quantities: ReadonlyArray<Dictionary.Quantity>,
92+
some: Dictionary.Quantity,
93+
not: Dictionary.Quantity,
94+
) {
95+
return quantities.some((quantity) => quantity === some) &&
96+
quantities.every((quantity) => quantity !== not);
97+
}
98+
function getNumber(
99+
determiners: ReadonlyArray<UnresolvedEnglish.Determiner>,
100+
): Dictionary.Quantity {
101+
const quantities = determiners.map(({ quantity }) => quantity);
102+
if (quantities.every((quantity) => quantity === "both")) {
103+
return "both";
104+
} else if (check(quantities, "singular", "plural")) {
105+
return "singular";
106+
} else if (check(quantities, "plural", "singular")) {
107+
return "plural";
108+
} else {
109+
// TODO: better error message
110+
throw new FilteredError(
111+
"chain of determiner including singular and plural",
112+
);
113+
}
114+
}
115+
function resolveDeterminer(
116+
determiner: UnresolvedEnglish.Determiner,
117+
): IterableResult<ResolvedEnglish.Determiner> {
118+
return simpleNounForms({
119+
singular: determiner.determiner,
120+
plural: determiner.plural,
121+
})
122+
.map((word) => ({
123+
...determiner,
124+
determiner: { word, emphasis: determiner.emphasis },
125+
}));
126+
}
127+
function resolveNoun(
128+
noun: UnresolvedEnglish.NounPhrase,
129+
place: Place,
130+
): IterableResult<ResolvedEnglish.NounPhrase> {
131+
switch (noun.type) {
132+
case "simple": {
133+
const number = getNumber(noun.determiners);
134+
return IterableResult.combine(
135+
fromNounForms(pronounForms(noun, place), number),
136+
IterableResult.combine(...noun.determiners.map(resolveDeterminer)),
137+
IterableResult.fromNullable(noun.postCompound)
138+
.flatMap((noun) => resolveNoun(noun, place)),
139+
IterableResult.combine(...noun.prepositions.map(resolvePreposition)),
140+
)
141+
.map((
142+
[{ noun: word, quantity }, determiners, postCompound, prepositions],
143+
) => ({
144+
...noun,
145+
determiners,
146+
noun: { word, emphasis: noun.wordEmphasis },
147+
quantity,
148+
emphasis: noun.phraseEmphasis,
149+
postCompound,
150+
prepositions,
151+
}));
152+
}
153+
case "compound":
154+
return IterableResult.combine(
155+
...noun.nouns.map((noun) => resolveNoun(noun, place)),
156+
)
157+
.map((nouns) => ({ ...noun, nouns }));
158+
}
159+
}
160+
function resolveComplement(
161+
complement: UnresolvedEnglish.Complement,
162+
): IterableResult<ResolvedEnglish.Complement> {
163+
switch (complement.type) {
164+
case "noun":
165+
return resolveNoun(complement.noun, "object")
166+
.map((noun) => ({ type: "noun", noun }));
167+
case "adjective":
168+
return IterableResult.single(complement);
169+
}
170+
}
171+
function resolvePreposition(
172+
preposition: UnresolvedEnglish.Preposition,
173+
): IterableResult<ResolvedEnglish.Preposition> {
174+
return resolveNoun(preposition.object, "object")
175+
.map((object) => ({ ...preposition, object }));
176+
}

src/translator_legacy/adjective.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,5 @@ export function combineAdjective(
118118
return [adjective];
119119
}
120120
}),
121-
emphasis: false,
122121
};
123122
}

src/translator_legacy/phrase.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ function adjectivePhrase(
131131
case "compound":
132132
if (modifier.adverbs.length === 0) {
133133
return {
134-
adjective: { ...adjective, emphasis: adjective.emphasis || emphasis },
134+
adjective: { ...adjective },
135135
inWayPhrase: modifier.inWayPhrase,
136136
};
137137
} else {

0 commit comments

Comments
 (0)