aboutsummaryrefslogtreecommitdiff
path: root/api/word.ts
blob: 2e07c98d51f165b864e417381785acbbcf5177c6 (plain)
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);
  }
}