1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
import Glossary from "./glossary.ts";
import APIBase from "./base.ts";
import Japanese, { JapaneseFormatter } from "./japanese.ts";
import "../util/string.ts";
import "../util/object.ts";
import { Tag, TagGroup, TokenTags } from "../search/tags.ts";
import { SearchWord } from "../search/types.ts";
import { recursiveValues } from "../util/object.ts";
// irregular stems taken from <https://en.wikipedia.org/wiki/Japanese_irregular_verbs#suru_and_kuru>
function irregularSuruStem(tags: TokenTags): string {
for (let i = 0, tag = tags[i]; i < tags.length; i++, tag = tags[i]) {
if (!recursiveValues(Tag.Inflection).includes(tag)) continue;
if (recursiveValues(Tag.Inflection.Reason).includes(tag)) continue;
if ([
Tag.Inflection.Polite.Masu,
Tag.Inflection.Suffix.Te,
Tag.Inflection.Tense.Past,
Tag.Inflection.Desirable.Itai, // part of Wikipedia's -ta form
Tag.Inflection.Negative,
Tag.Inflection.Desirable.Volitional,
Tag.Inflection.Command,
].includes(tag as any)) return "し";
if ([
Tag.Inflection.Passive,
Tag.Inflection.Causative,
].includes(tag as any)) return "さ";
// wikipedia has できる as the potential form for する, but できる here
// means it's already foobar'd
break;
}
return "す";
}
function irregularKuruStem(tags: TokenTags): string {
for (let i = 0, tag = tags[i]; i < tags.length; i++, tag = tags[i]) {
if (!recursiveValues(Tag.Inflection).includes(tag)) continue;
if (recursiveValues(Tag.Inflection.Reason).includes(tag)) continue;
if ([
Tag.Inflection.Polite.Masu,
Tag.Inflection.Suffix.Te,
Tag.Inflection.Tense.Past,
Tag.Inflection.Desirable.Itai, // part of Wikipedia's -ta form
].includes(tag as any)) return "き";
if ([
Tag.Inflection.Negative,
Tag.Inflection.Desirable.Volitional,
Tag.Inflection.Passive,
Tag.Inflection.Causative,
Tag.Inflection.Potential,
Tag.Inflection.Command,
].includes(tag as any)) return "こ";
break;
}
return "く";
}
export default class Word extends APIBase {
/** @prop dictionary form of verb if this word is a verb */
protected base: Japanese;
/** @prop word as written in parent sentence */
protected text: Japanese;
/** @prop this.furigana should output kanji with reading */
protected outputKanji: boolean = true;
/** @prop this word represents an unrecognized sentence part between recognized terms */
protected filler: boolean;
constructor(input: string | SearchWord) {
super();
if (typeof input === "string") {
this.filler = true;
input = input as string;
this.base = new Japanese(input, input);
this.text = this.base;
this.outputKanji = false;
} else {
this.filler = false;
input = input as SearchWord;
this.base = new Japanese(input.writing, input.reading);
if (input.tags.anyOf(TagGroup.Conjugable as string[])) {
// transfer conjugation from input.source to both dictionary reading and writing for furigana
var writingCommon = input.writing.cmpLen(input.source);
var readingCommon = input.reading.cmpLen(input.source);
var stemLength = Math.max(writingCommon, readingCommon);
var base = input[writingCommon > readingCommon ? "writing" : "reading"].substring(stemLength);
var conjugation = input.source.substring(stemLength);
// special reading for irregular verbs
var reading = input.reading;
if (input.writing == '来る') reading = irregularKuruStem(input.tags) + conjugation;
else if (input.writing == '為る') reading = irregularSuruStem(input.tags) + conjugation;
else reading = reading.replaceLast(base, conjugation);
// generate conjugated version of verb with kanji
this.text = new Japanese(input.writing.replaceLast(base, conjugation), reading);
} else {
this.text = this.base;
}
this.outputKanji = input.source.hasKanji(); // only output kanji if input also uses kanji
}
}
furigana(format: JapaneseFormatter) {
if (!this.outputKanji) return this.text.reading;
else return this.text.furigana(format);
}
async glossary() {
// TODO: output nothing if this.filler == true
return new Glossary().withParent(await this.api);
}
}
|