Skip to content

Commit ad4d19d

Browse files
committed
rewrite sentence translator
1 parent 4b60801 commit ad4d19d

1 file changed

Lines changed: 232 additions & 0 deletions

File tree

src/translator/sentence.ts

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
import { IterableResult } from "../compound.ts";
2+
import { dictionary } from "../dictionary/dictionary.ts";
3+
import { nullableAsArray } from "../misc/misc.ts";
4+
import * as TokiPona from "../parser/ast.ts";
5+
import { definitionAsPlainString } from "../translator_legacy/as_string.ts";
6+
import * as English from "./ast.ts";
7+
import { clause, contextClause, unwrapSingleWord } from "./clause.ts";
8+
import { fromSimpleDefinition, getReduplicationCount } from "./word_unit.ts";
9+
import { TranslationTodoError } from "./error.ts";
10+
import { noEmphasis, word } from "./word.ts";
11+
12+
function filler(filler: TokiPona.Filler) {
13+
switch (filler.type) {
14+
case "word":
15+
case "long word": {
16+
let length: number;
17+
switch (filler.type) {
18+
case "word":
19+
length = 1;
20+
break;
21+
case "long word":
22+
length = filler.length;
23+
break;
24+
}
25+
return IterableResult.fromArray(dictionary.get(filler.word)!.definitions)
26+
.filterMap((definition) => {
27+
if (definition.type === "filler") {
28+
const { before, repeat, after } = definition;
29+
return `${before}${repeat.repeat(length)}${after}`;
30+
} else {
31+
return null;
32+
}
33+
});
34+
}
35+
case "reduplicated a":
36+
return IterableResult.single("ha".repeat(filler.count));
37+
}
38+
}
39+
function emphasisAsPunctuation(
40+
options: Readonly<{
41+
emphasis: null | TokiPona.Emphasis;
42+
interrogative: boolean;
43+
originalPunctuation: string;
44+
}>,
45+
) {
46+
const { emphasis, interrogative, originalPunctuation } = options;
47+
if (emphasis == null) {
48+
if (interrogative) {
49+
return "?";
50+
} else {
51+
return originalPunctuation;
52+
}
53+
} else {
54+
const questionMark = interrogative ? "?" : "";
55+
let exclamationMark: string;
56+
switch (emphasis.type) {
57+
case "word":
58+
exclamationMark = "!";
59+
break;
60+
case "long word":
61+
exclamationMark = "!".repeat(emphasis.length);
62+
break;
63+
}
64+
return `${questionMark}${exclamationMark}`;
65+
}
66+
}
67+
68+
function sentence(
69+
sentence: TokiPona.Sentence,
70+
isFinal: boolean,
71+
): IterableResult<English.Sentence> {
72+
if (sentence.interrogative === "x ala x") {
73+
return IterableResult.errors([new TranslationTodoError("x ala x")]);
74+
}
75+
const punctuation = !isFinal && sentence.punctuation === ""
76+
? ","
77+
: sentence.punctuation;
78+
switch (sentence.type) {
79+
case "simple": {
80+
let startingAdverb: IterableResult<null | English.Word>;
81+
let startingConjunction: null | English.Word;
82+
let contextClauses: ReadonlyArray<TokiPona.ContextClause>;
83+
if (sentence.startingParticle != null) {
84+
const { startingParticle } = sentence;
85+
const emphasis = startingParticle.emphasis != null;
86+
const reduplicationCount = getReduplicationCount(startingParticle);
87+
contextClauses = sentence.contextClauses;
88+
switch (sentence.startingParticle.word as "taso" | "kin" | "anu") {
89+
case "taso":
90+
startingAdverb = IterableResult.single(null);
91+
startingConjunction = word({
92+
reduplicationCount,
93+
emphasis,
94+
word: "but",
95+
});
96+
break;
97+
case "kin":
98+
startingAdverb = fromSimpleDefinition(
99+
startingParticle,
100+
(definition) =>
101+
definition.type === "adverb" ? definition.adverb : null,
102+
);
103+
startingConjunction = null;
104+
break;
105+
case "anu":
106+
startingAdverb = IterableResult.single(null);
107+
startingConjunction = word({
108+
reduplicationCount,
109+
emphasis,
110+
word: "or",
111+
});
112+
break;
113+
}
114+
} else if (
115+
sentence.contextClauses.length > 0 &&
116+
sentence.contextClauses[0].type === "anu"
117+
) {
118+
const anu = sentence.contextClauses[0];
119+
startingAdverb = IterableResult.single(null);
120+
startingConjunction = word({
121+
reduplicationCount: getReduplicationCount(anu.anu),
122+
emphasis: anu.anu.emphasis != null,
123+
word: "or",
124+
});
125+
contextClauses = sentence.contextClauses.slice(1);
126+
} else {
127+
startingAdverb = IterableResult.single(null);
128+
startingConjunction = null;
129+
contextClauses = sentence.contextClauses;
130+
}
131+
const useAnuSeme = nullableAsArray(sentence.anuSeme)
132+
.map((seme): English.Clause => ({
133+
type: "interjection",
134+
interjection: word({
135+
word: "right",
136+
reduplicationCount: getReduplicationCount(seme),
137+
emphasis: seme.emphasis != null,
138+
}),
139+
}));
140+
const interjectionClause: IterableResult<English.Clause> =
141+
contextClauses.length === 0 &&
142+
sentence.startingParticle == null
143+
? IterableResult.fromNullable(unwrapSingleWord(sentence.finalClause))
144+
.flatMap((wordUnit) =>
145+
fromSimpleDefinition(
146+
wordUnit,
147+
(definition) =>
148+
definition.type === "interjection"
149+
? definition.interjection
150+
: null,
151+
)
152+
)
153+
.map((interjection): English.Clause => ({
154+
type: "interjection",
155+
interjection,
156+
}))
157+
: IterableResult.empty();
158+
const clauses = IterableResult.combine(
159+
startingAdverb,
160+
IterableResult.combine(...contextClauses.map(contextClause))
161+
.map((clause) => clause.flat()),
162+
IterableResult.concat(interjectionClause, clause(sentence.finalClause)),
163+
)
164+
.map(([adverb, contextClauses, lastClause]) => [
165+
...nullableAsArray(adverb)
166+
.map((adverb): English.Clause => ({ type: "adverb", adverb })),
167+
...contextClauses,
168+
lastClause,
169+
...useAnuSeme,
170+
]);
171+
let withConjunction: IterableResult<ReadonlyArray<English.Clause>>;
172+
if (startingConjunction != null) {
173+
withConjunction = clauses
174+
.map<ReadonlyArray<English.Clause>>(([first, ...rest]) => [
175+
{
176+
type: "dependent",
177+
conjunction: startingConjunction,
178+
clause: first,
179+
},
180+
...rest,
181+
]);
182+
} else {
183+
withConjunction = clauses;
184+
}
185+
const usePunctuation = emphasisAsPunctuation({
186+
emphasis: sentence.emphasis,
187+
interrogative: sentence.interrogative != null,
188+
originalPunctuation: punctuation,
189+
});
190+
return withConjunction.map((clauses): English.Sentence => ({
191+
clauses,
192+
punctuation: usePunctuation,
193+
}));
194+
}
195+
case "filler":
196+
return filler(sentence.filler)
197+
.map((interjection): English.Sentence => ({
198+
clauses: [
199+
{
200+
type: "interjection",
201+
interjection: noEmphasis(interjection),
202+
},
203+
],
204+
punctuation,
205+
}));
206+
}
207+
}
208+
export function multipleSentences(
209+
sentences: TokiPona.MultipleSentences,
210+
): IterableResult<English.MultipleSentences> {
211+
switch (sentences.type) {
212+
case "single word": {
213+
const { word } = sentences;
214+
return IterableResult.fromArray(dictionary.get(word)!.definitions)
215+
.flatMap(definitionAsPlainString)
216+
.map((definition): English.MultipleSentences => ({
217+
type: "free form",
218+
text: definition,
219+
}));
220+
}
221+
case "sentences":
222+
return IterableResult.combine(
223+
...sentences.sentences.map((value, i) =>
224+
sentence(value, i === sentences.sentences.length - 1)
225+
),
226+
)
227+
.map((sentences): English.MultipleSentences => ({
228+
type: "sentences",
229+
sentences,
230+
}));
231+
}
232+
}

0 commit comments

Comments
 (0)