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"; import Sentence from "./sentence.ts"; // TODO: better irregular reading handling (should also work for counter words / 入る) // irregular stems taken from function irregularSuru(tags: TokenTags, conjugation: string): 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.Desirable.Itai, // part of Wikipedia's -ta form Tag.Inflection.Command, ].includes(tag as any)) return "し" + conjugation; if ([ Tag.Inflection.Passive, Tag.Inflection.Causative, ].includes(tag as any)) return "さ" + conjugation; // wikipedia has できる as the potential form for する, but できる here // means it's already foobar'd break; } return conjugation; } function irregularKuru(tags: TokenTags, conjugation: string): 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 "き" + conjugation; 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 "こ" + conjugation; 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 word represents an unrecognized sentence part between recognized terms */ protected filler: boolean; private _resolveParent: (sentence: Sentence) => void = _ => {}; /** @prop parent sentence */ protected parent = new Promise(res => this._resolveParent = res); /** @prop length of word in sentence */ public length: number; /** @prop (conjugated) writing of term (*may* contain kanji) */ public writing: string; /** @prop (conjugated) reading of term (kana-only) */ public reading: string; /** @prop dictionary id for term */ public id: number = -1; 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; } 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 = irregularKuru(input.tags, conjugation); else if (input.writing == '為る') reading = irregularSuru(input.tags, conjugation); else reading = reading.replaceLast(base, conjugation); // generate conjugated version of verb with kanji this.text = new Japanese(input.source, reading); } else { // add dictionary reading to this.source as writing (could contain kanji) this.text = new Japanese(input.source, this.base.reading); } this.id = input.id; } this.writing = this.text.writing; this.length = this.text.writing.length; this.reading = this.text.reading; } furigana(format: JapaneseFormatter) { // don't create furigana for filler/kana only terms if (this.text.writing == this.text.reading) return this.text.writing; return this.text.furigana(format); } async glossary() { // TODO: output nothing if this.filler == true return new Glossary().withAPI(await this.api); } /** @summary check if this word is written as ~ */ public written(as: string) { return this.text.writing == as || this.base.writing == as; } /** @summary check if this word is read as ~ */ public read(as: string) { return this.text.reading == as || this.base.reading == as; } /** @summary ignore this word for currently logged in user and refresh the sentence */ async ignore() { await (await this.api)["setTermPriority"](this.base.writing, this.base.reading, -1); await (await this.parent).update(); } /** @summary set parent sentence for this word */ public withParent(parent: Sentence) { this._resolveParent(parent); return this; } }