c2cd5e01e1
El SDK CI - dev / build-and-test (pull_request) Successful in 3m34s
elb.el: - Auto-detect Homebrew OpenSSL (-L$(brew --prefix openssl)/lib) so -lssl resolves on macOS without manual flags; no-op on Linux - Add -include elp-c-decls.h when present in out_dir: resolves undeclared cross-module calls in packages like ELP that lack explicit imports ELP source: - Add import "morphology.el" to all 29 language morphology modules - Add language module imports to morphology.el (all langs it dispatches to) These were missing since ELP was originally built as a monolithic unit
451 lines
23 KiB
EmacsLisp
451 lines
23 KiB
EmacsLisp
// morphology-ja.el - Japanese morphology: verb conjugation and noun particles.
|
|
//
|
|
// Japanese is SOV, agglutinative, pro-drop. Morphology is suffix-chain based.
|
|
//
|
|
// Key facts:
|
|
// - Nouns do not inflect; grammatical relations are marked by postpositional
|
|
// particles attached after the noun.
|
|
// - Verbs inflect for tense, polarity, and politeness.
|
|
// - Two main verb groups: Ichidan (vowel-stem, Group 2) and Godan (consonant-
|
|
// stem, Group 1), plus a small set of irregular verbs.
|
|
// - Politeness levels: plain (dictionary/casual) and polite (masu-form).
|
|
// - Questions are formed by appending the sentence-final particle か (ka).
|
|
//
|
|
// Depends on: (no dependencies - standalone morphology module)
|
|
|
|
// ── Verb group classification ─────────────────────────────────────────────────
|
|
//
|
|
// Returns "ichidan" | "godan" | "irregular"
|
|
//
|
|
// Irregular verbs are checked first. Ichidan verbs end in -る and have a
|
|
// vowel immediately before the る. Everything else is Godan.
|
|
//
|
|
// Note: this is a heuristic classifier for romanized input. For production use
|
|
// with native kana/kanji forms, the dictionary form (辞書形) must be consulted.
|
|
|
|
import "morphology.el"
|
|
fn ja_verb_group(dict_form: String) -> String {
|
|
// Irregular verbs (exact match on dictionary form)
|
|
if str_eq(dict_form, "する") { return "irregular" }
|
|
if str_eq(dict_form, "くる") { return "irregular" }
|
|
if str_eq(dict_form, "くる") { return "irregular" }
|
|
if str_eq(dict_form, "いる") { return "irregular" }
|
|
if str_eq(dict_form, "ある") { return "irregular" }
|
|
if str_eq(dict_form, "だ") { return "irregular" }
|
|
// Romanized irregulars
|
|
if str_eq(dict_form, "suru") { return "irregular" }
|
|
if str_eq(dict_form, "kuru") { return "irregular" }
|
|
if str_eq(dict_form, "iru") { return "irregular" }
|
|
if str_eq(dict_form, "aru") { return "irregular" }
|
|
if str_eq(dict_form, "da") { return "irregular" }
|
|
|
|
// Ichidan: ends in -る (-ru) and has a vowel before it.
|
|
// We test using native kana ending and the romanized -eru / -iru pattern.
|
|
if str_ends_with(dict_form, "る") {
|
|
// Any kana-form ending in る that is not in the irregular list is
|
|
// heuristically classified as Ichidan when the preceding mora ends in
|
|
// a vowel sound. For the romanized path we check -eru and -iru endings.
|
|
return "ichidan"
|
|
}
|
|
if str_ends_with(dict_form, "eru") { return "ichidan" }
|
|
if str_ends_with(dict_form, "iru") { return "ichidan" }
|
|
|
|
return "godan"
|
|
}
|
|
|
|
// ── Ichidan stem extraction ───────────────────────────────────────────────────
|
|
//
|
|
// Strip the final -る (-ru) from an Ichidan dictionary form.
|
|
// 食べる → 食べ taberu → tabe
|
|
|
|
fn ja_ichidan_stem(dict_form: String) -> String {
|
|
if str_ends_with(dict_form, "る") {
|
|
let n: Int = str_len(dict_form)
|
|
// str_slice operates on byte offsets; る is 3 UTF-8 bytes.
|
|
// We trust the runtime to handle Unicode correctly via str_drop_last.
|
|
return str_drop_last(dict_form, 1)
|
|
}
|
|
if str_ends_with(dict_form, "ru") {
|
|
let n: Int = str_len(dict_form)
|
|
return str_slice(dict_form, 0, n - 2)
|
|
}
|
|
return dict_form
|
|
}
|
|
|
|
// ── Godan stem changes ────────────────────────────────────────────────────────
|
|
//
|
|
// Godan verbs mutate their final kana to a different row before attaching a
|
|
// suffix. The "row" parameter selects which stem form is needed:
|
|
//
|
|
// "i" - the i-row (連用形 ren'yōkei): for polite forms, te-form base
|
|
// "a" - the a-row (未然形 mizenkei): for negative
|
|
// "te" - te/ta-form (special sound changes apply)
|
|
//
|
|
// Final kana → i-row: く→き ぐ→ぎ す→し つ→ち ぬ→に ぶ→び む→み る→り う→い
|
|
// Final kana → a-row: く→か ぐ→が す→さ つ→た ぬ→な ぶ→ば む→ま る→ら う→わ
|
|
// Te/ta-form: く→い ぐ→い す→し つ→っ ぬ→ん ぶ→ん む→ん る→っ う→っ
|
|
|
|
fn ja_godan_stem_change(dict_form: String, row: String) -> String {
|
|
let n: Int = str_len(dict_form)
|
|
if n == 0 { return dict_form }
|
|
|
|
// i-row (polite stem / ren'yōkei)
|
|
if str_eq(row, "i") {
|
|
if str_ends_with(dict_form, "く") { return str_drop_last(dict_form, 1) + "き" }
|
|
if str_ends_with(dict_form, "ぐ") { return str_drop_last(dict_form, 1) + "ぎ" }
|
|
if str_ends_with(dict_form, "す") { return str_drop_last(dict_form, 1) + "し" }
|
|
if str_ends_with(dict_form, "つ") { return str_drop_last(dict_form, 1) + "ち" }
|
|
if str_ends_with(dict_form, "ぬ") { return str_drop_last(dict_form, 1) + "に" }
|
|
if str_ends_with(dict_form, "ぶ") { return str_drop_last(dict_form, 1) + "び" }
|
|
if str_ends_with(dict_form, "む") { return str_drop_last(dict_form, 1) + "み" }
|
|
if str_ends_with(dict_form, "る") { return str_drop_last(dict_form, 1) + "り" }
|
|
if str_ends_with(dict_form, "う") { return str_drop_last(dict_form, 1) + "い" }
|
|
// Romanized fallbacks
|
|
if str_ends_with(dict_form, "ku") { return str_drop_last(dict_form, 2) + "ki" }
|
|
if str_ends_with(dict_form, "gu") { return str_drop_last(dict_form, 2) + "gi" }
|
|
if str_ends_with(dict_form, "su") { return str_drop_last(dict_form, 2) + "shi" }
|
|
if str_ends_with(dict_form, "tsu") { return str_drop_last(dict_form, 3) + "chi" }
|
|
if str_ends_with(dict_form, "nu") { return str_drop_last(dict_form, 2) + "ni" }
|
|
if str_ends_with(dict_form, "bu") { return str_drop_last(dict_form, 2) + "bi" }
|
|
if str_ends_with(dict_form, "mu") { return str_drop_last(dict_form, 2) + "mi" }
|
|
if str_ends_with(dict_form, "ru") { return str_drop_last(dict_form, 2) + "ri" }
|
|
if str_ends_with(dict_form, "u") { return str_drop_last(dict_form, 1) + "i" }
|
|
return dict_form
|
|
}
|
|
|
|
// a-row (negative stem / mizenkei)
|
|
if str_eq(row, "a") {
|
|
if str_ends_with(dict_form, "く") { return str_drop_last(dict_form, 1) + "か" }
|
|
if str_ends_with(dict_form, "ぐ") { return str_drop_last(dict_form, 1) + "が" }
|
|
if str_ends_with(dict_form, "す") { return str_drop_last(dict_form, 1) + "さ" }
|
|
if str_ends_with(dict_form, "つ") { return str_drop_last(dict_form, 1) + "た" }
|
|
if str_ends_with(dict_form, "ぬ") { return str_drop_last(dict_form, 1) + "な" }
|
|
if str_ends_with(dict_form, "ぶ") { return str_drop_last(dict_form, 1) + "ば" }
|
|
if str_ends_with(dict_form, "む") { return str_drop_last(dict_form, 1) + "ま" }
|
|
if str_ends_with(dict_form, "る") { return str_drop_last(dict_form, 1) + "ら" }
|
|
if str_ends_with(dict_form, "う") { return str_drop_last(dict_form, 1) + "わ" }
|
|
// Romanized fallbacks
|
|
if str_ends_with(dict_form, "ku") { return str_drop_last(dict_form, 2) + "ka" }
|
|
if str_ends_with(dict_form, "gu") { return str_drop_last(dict_form, 2) + "ga" }
|
|
if str_ends_with(dict_form, "su") { return str_drop_last(dict_form, 2) + "sa" }
|
|
if str_ends_with(dict_form, "tsu") { return str_drop_last(dict_form, 3) + "ta" }
|
|
if str_ends_with(dict_form, "nu") { return str_drop_last(dict_form, 2) + "na" }
|
|
if str_ends_with(dict_form, "bu") { return str_drop_last(dict_form, 2) + "ba" }
|
|
if str_ends_with(dict_form, "mu") { return str_drop_last(dict_form, 2) + "ma" }
|
|
if str_ends_with(dict_form, "ru") { return str_drop_last(dict_form, 2) + "ra" }
|
|
if str_ends_with(dict_form, "u") { return str_drop_last(dict_form, 1) + "wa" }
|
|
return dict_form
|
|
}
|
|
|
|
// te/ta-row (te-form and plain past; special euphonic changes)
|
|
// Sound changes: く→い, ぐ→い (voiced), す→し, つ/る/う→っ, ぬ/ぶ/む→ん
|
|
if str_eq(row, "te") {
|
|
if str_ends_with(dict_form, "く") { return str_drop_last(dict_form, 1) + "い" }
|
|
if str_ends_with(dict_form, "ぐ") { return str_drop_last(dict_form, 1) + "い" }
|
|
if str_ends_with(dict_form, "す") { return str_drop_last(dict_form, 1) + "し" }
|
|
if str_ends_with(dict_form, "つ") { return str_drop_last(dict_form, 1) + "っ" }
|
|
if str_ends_with(dict_form, "ぬ") { return str_drop_last(dict_form, 1) + "ん" }
|
|
if str_ends_with(dict_form, "ぶ") { return str_drop_last(dict_form, 1) + "ん" }
|
|
if str_ends_with(dict_form, "む") { return str_drop_last(dict_form, 1) + "ん" }
|
|
if str_ends_with(dict_form, "る") { return str_drop_last(dict_form, 1) + "っ" }
|
|
if str_ends_with(dict_form, "う") { return str_drop_last(dict_form, 1) + "っ" }
|
|
// Romanized fallbacks
|
|
if str_ends_with(dict_form, "ku") { return str_drop_last(dict_form, 2) + "i" }
|
|
if str_ends_with(dict_form, "gu") { return str_drop_last(dict_form, 2) + "i" }
|
|
if str_ends_with(dict_form, "su") { return str_drop_last(dict_form, 2) + "shi" }
|
|
if str_ends_with(dict_form, "tsu") { return str_drop_last(dict_form, 3) + "tt" }
|
|
if str_ends_with(dict_form, "nu") { return str_drop_last(dict_form, 2) + "n" }
|
|
if str_ends_with(dict_form, "bu") { return str_drop_last(dict_form, 2) + "n" }
|
|
if str_ends_with(dict_form, "mu") { return str_drop_last(dict_form, 2) + "n" }
|
|
if str_ends_with(dict_form, "ru") { return str_drop_last(dict_form, 2) + "tt" }
|
|
if str_ends_with(dict_form, "u") { return str_drop_last(dict_form, 1) + "tt" }
|
|
return dict_form
|
|
}
|
|
|
|
return dict_form
|
|
}
|
|
|
|
// ── Verb conjugation ──────────────────────────────────────────────────────────
|
|
//
|
|
// ja_conjugate(dict_form, form) -> String
|
|
//
|
|
// form values:
|
|
// "present" - plain non-past (dictionary form)
|
|
// "past" - plain past
|
|
// "negative" - plain negative
|
|
// "volitional" - plain volitional (let's …)
|
|
// "polite" - polite non-past (masu-form)
|
|
// "polite-past" - polite past (mashita-form)
|
|
// "polite-neg" - polite negative (masen-form)
|
|
// "te" - te-form (connective / gerund)
|
|
|
|
fn ja_conjugate(dict_form: String, form: String) -> String {
|
|
let group: String = ja_verb_group(dict_form)
|
|
|
|
// ── Irregular verbs ───────────────────────────────────────────────────────
|
|
if str_eq(group, "irregular") {
|
|
// する / suru (to do)
|
|
if str_eq(dict_form, "する") {
|
|
if str_eq(form, "present") { return "する" }
|
|
if str_eq(form, "past") { return "した" }
|
|
if str_eq(form, "negative") { return "しない" }
|
|
if str_eq(form, "volitional") { return "しよう" }
|
|
if str_eq(form, "polite") { return "します" }
|
|
if str_eq(form, "polite-past") { return "しました" }
|
|
if str_eq(form, "polite-neg") { return "しません" }
|
|
if str_eq(form, "te") { return "して" }
|
|
return dict_form
|
|
}
|
|
if str_eq(dict_form, "suru") {
|
|
if str_eq(form, "present") { return "suru" }
|
|
if str_eq(form, "past") { return "shita" }
|
|
if str_eq(form, "negative") { return "shinai" }
|
|
if str_eq(form, "volitional") { return "shiyou" }
|
|
if str_eq(form, "polite") { return "shimasu" }
|
|
if str_eq(form, "polite-past") { return "shimashita" }
|
|
if str_eq(form, "polite-neg") { return "shimasen" }
|
|
if str_eq(form, "te") { return "shite" }
|
|
return dict_form
|
|
}
|
|
|
|
// くる / kuru (to come)
|
|
if str_eq(dict_form, "くる") {
|
|
if str_eq(form, "present") { return "くる" }
|
|
if str_eq(form, "past") { return "きた" }
|
|
if str_eq(form, "negative") { return "こない" }
|
|
if str_eq(form, "volitional") { return "こよう" }
|
|
if str_eq(form, "polite") { return "きます" }
|
|
if str_eq(form, "polite-past") { return "きました" }
|
|
if str_eq(form, "polite-neg") { return "きません" }
|
|
if str_eq(form, "te") { return "きて" }
|
|
return dict_form
|
|
}
|
|
if str_eq(dict_form, "kuru") {
|
|
if str_eq(form, "present") { return "kuru" }
|
|
if str_eq(form, "past") { return "kita" }
|
|
if str_eq(form, "negative") { return "konai" }
|
|
if str_eq(form, "volitional") { return "koyou" }
|
|
if str_eq(form, "polite") { return "kimasu" }
|
|
if str_eq(form, "polite-past") { return "kimashita" }
|
|
if str_eq(form, "polite-neg") { return "kimasen" }
|
|
if str_eq(form, "te") { return "kite" }
|
|
return dict_form
|
|
}
|
|
|
|
// いる / iru (to be / exist, animate)
|
|
if str_eq(dict_form, "いる") {
|
|
if str_eq(form, "present") { return "いる" }
|
|
if str_eq(form, "past") { return "いた" }
|
|
if str_eq(form, "negative") { return "いない" }
|
|
if str_eq(form, "volitional") { return "いよう" }
|
|
if str_eq(form, "polite") { return "います" }
|
|
if str_eq(form, "polite-past") { return "いました" }
|
|
if str_eq(form, "polite-neg") { return "いません" }
|
|
if str_eq(form, "te") { return "いて" }
|
|
return dict_form
|
|
}
|
|
if str_eq(dict_form, "iru") {
|
|
if str_eq(form, "present") { return "iru" }
|
|
if str_eq(form, "past") { return "ita" }
|
|
if str_eq(form, "negative") { return "inai" }
|
|
if str_eq(form, "volitional") { return "iyou" }
|
|
if str_eq(form, "polite") { return "imasu" }
|
|
if str_eq(form, "polite-past") { return "imashita" }
|
|
if str_eq(form, "polite-neg") { return "imasen" }
|
|
if str_eq(form, "te") { return "ite" }
|
|
return dict_form
|
|
}
|
|
|
|
// ある / aru (to be / exist, inanimate)
|
|
if str_eq(dict_form, "ある") {
|
|
if str_eq(form, "present") { return "ある" }
|
|
if str_eq(form, "past") { return "あった" }
|
|
if str_eq(form, "negative") { return "ない" }
|
|
if str_eq(form, "volitional") { return "あろう" }
|
|
if str_eq(form, "polite") { return "あります" }
|
|
if str_eq(form, "polite-past") { return "ありました" }
|
|
if str_eq(form, "polite-neg") { return "ありません" }
|
|
if str_eq(form, "te") { return "あって" }
|
|
return dict_form
|
|
}
|
|
if str_eq(dict_form, "aru") {
|
|
if str_eq(form, "present") { return "aru" }
|
|
if str_eq(form, "past") { return "atta" }
|
|
if str_eq(form, "negative") { return "nai" }
|
|
if str_eq(form, "volitional") { return "arou" }
|
|
if str_eq(form, "polite") { return "arimasu" }
|
|
if str_eq(form, "polite-past") { return "arimashita" }
|
|
if str_eq(form, "polite-neg") { return "arimasen" }
|
|
if str_eq(form, "te") { return "atte" }
|
|
return dict_form
|
|
}
|
|
|
|
// だ / da (copula)
|
|
if str_eq(dict_form, "だ") {
|
|
if str_eq(form, "present") { return "だ" }
|
|
if str_eq(form, "past") { return "だった" }
|
|
if str_eq(form, "negative") { return "ではない" }
|
|
if str_eq(form, "volitional") { return "だろう" }
|
|
if str_eq(form, "polite") { return "です" }
|
|
if str_eq(form, "polite-past") { return "でした" }
|
|
if str_eq(form, "polite-neg") { return "ではありません" }
|
|
if str_eq(form, "te") { return "で" }
|
|
return dict_form
|
|
}
|
|
if str_eq(dict_form, "da") {
|
|
if str_eq(form, "present") { return "da" }
|
|
if str_eq(form, "past") { return "datta" }
|
|
if str_eq(form, "negative") { return "dewanai" }
|
|
if str_eq(form, "volitional") { return "darou" }
|
|
if str_eq(form, "polite") { return "desu" }
|
|
if str_eq(form, "polite-past") { return "deshita" }
|
|
if str_eq(form, "polite-neg") { return "dewaarimarsen" }
|
|
if str_eq(form, "te") { return "de" }
|
|
return dict_form
|
|
}
|
|
|
|
// Unknown irregular — fall through to base form
|
|
return dict_form
|
|
}
|
|
|
|
// ── Ichidan verbs ─────────────────────────────────────────────────────────
|
|
if str_eq(group, "ichidan") {
|
|
let stem: String = ja_ichidan_stem(dict_form)
|
|
|
|
if str_eq(form, "present") { return dict_form }
|
|
if str_eq(form, "past") { return stem + "た" }
|
|
if str_eq(form, "negative") { return stem + "ない" }
|
|
if str_eq(form, "volitional") { return stem + "よう" }
|
|
if str_eq(form, "polite") { return stem + "ます" }
|
|
if str_eq(form, "polite-past") { return stem + "ました" }
|
|
if str_eq(form, "polite-neg") { return stem + "ません" }
|
|
if str_eq(form, "te") { return stem + "て" }
|
|
return dict_form
|
|
}
|
|
|
|
// ── Godan verbs ───────────────────────────────────────────────────────────
|
|
// Godan plain present: dictionary form unchanged
|
|
if str_eq(form, "present") { return dict_form }
|
|
|
|
// Godan polite forms use the i-row stem + masu endings
|
|
if str_eq(form, "polite") {
|
|
let istem: String = ja_godan_stem_change(dict_form, "i")
|
|
return istem + "ます"
|
|
}
|
|
if str_eq(form, "polite-past") {
|
|
let istem: String = ja_godan_stem_change(dict_form, "i")
|
|
return istem + "ました"
|
|
}
|
|
if str_eq(form, "polite-neg") {
|
|
let istem: String = ja_godan_stem_change(dict_form, "i")
|
|
return istem + "ません"
|
|
}
|
|
|
|
// Godan plain negative uses the a-row stem + nai
|
|
if str_eq(form, "negative") {
|
|
let astem: String = ja_godan_stem_change(dict_form, "a")
|
|
return astem + "ない"
|
|
}
|
|
|
|
// Godan volitional: i-row + ou (う ending → おう, others → ろう via i-stem)
|
|
if str_eq(form, "volitional") {
|
|
// う-verbs: drop final う and add おう
|
|
if str_ends_with(dict_form, "う") {
|
|
return str_drop_last(dict_form, 1) + "おう"
|
|
}
|
|
let istem: String = ja_godan_stem_change(dict_form, "i")
|
|
return istem + "ろう"
|
|
}
|
|
|
|
// Godan te-form: euphonic te-row stem + て (voiced ぐ ending → いで)
|
|
if str_eq(form, "te") {
|
|
let tstem: String = ja_godan_stem_change(dict_form, "te")
|
|
// Voiced consonants (ぐ) use で instead of て
|
|
if str_ends_with(dict_form, "ぐ") { return tstem + "いで" }
|
|
if str_ends_with(dict_form, "gu") { return tstem + "ide" }
|
|
// Nasal assimilation: ぬ/ぶ/む → んで
|
|
if str_ends_with(dict_form, "ぬ") { return tstem + "んで" }
|
|
if str_ends_with(dict_form, "ぶ") { return tstem + "んで" }
|
|
if str_ends_with(dict_form, "む") { return tstem + "んで" }
|
|
if str_ends_with(dict_form, "nu") { return tstem + "nde" }
|
|
if str_ends_with(dict_form, "bu") { return tstem + "nde" }
|
|
if str_ends_with(dict_form, "mu") { return tstem + "nde" }
|
|
// す → して
|
|
if str_ends_with(dict_form, "す") { return tstem + "して" }
|
|
if str_ends_with(dict_form, "su") { return tstem + "shite" }
|
|
// く → いて (tstem already has い)
|
|
if str_ends_with(dict_form, "く") { return tstem + "て" }
|
|
if str_ends_with(dict_form, "ku") { return tstem + "te" }
|
|
// つ/る/う → って
|
|
return tstem + "て"
|
|
}
|
|
|
|
// Godan plain past: same stem changes as te-form, then た/だ
|
|
if str_eq(form, "past") {
|
|
let tstem: String = ja_godan_stem_change(dict_form, "te")
|
|
if str_ends_with(dict_form, "ぐ") { return tstem + "いだ" }
|
|
if str_ends_with(dict_form, "gu") { return tstem + "ida" }
|
|
if str_ends_with(dict_form, "ぬ") { return tstem + "んだ" }
|
|
if str_ends_with(dict_form, "ぶ") { return tstem + "んだ" }
|
|
if str_ends_with(dict_form, "む") { return tstem + "んだ" }
|
|
if str_ends_with(dict_form, "nu") { return tstem + "nda" }
|
|
if str_ends_with(dict_form, "bu") { return tstem + "nda" }
|
|
if str_ends_with(dict_form, "mu") { return tstem + "nda" }
|
|
if str_ends_with(dict_form, "す") { return tstem + "した" }
|
|
if str_ends_with(dict_form, "su") { return tstem + "shita" }
|
|
if str_ends_with(dict_form, "く") { return tstem + "た" }
|
|
if str_ends_with(dict_form, "ku") { return tstem + "ta" }
|
|
return tstem + "た"
|
|
}
|
|
|
|
return dict_form
|
|
}
|
|
|
|
// ── Case particles ────────────────────────────────────────────────────────────
|
|
//
|
|
// Japanese nouns do not inflect; case is indicated by a postpositional particle
|
|
// placed directly after the noun.
|
|
|
|
fn ja_particle(gram_case: String) -> String {
|
|
if str_eq(gram_case, "nominative") { return "が" }
|
|
if str_eq(gram_case, "accusative") { return "を" }
|
|
if str_eq(gram_case, "dative") { return "に" }
|
|
if str_eq(gram_case, "genitive") { return "の" }
|
|
if str_eq(gram_case, "topic") { return "は" }
|
|
if str_eq(gram_case, "instrumental") { return "で" }
|
|
if str_eq(gram_case, "locative") { return "に" }
|
|
if str_eq(gram_case, "ablative") { return "から" }
|
|
if str_eq(gram_case, "direction") { return "へ" }
|
|
if str_eq(gram_case, "comitative") { return "と" }
|
|
return ""
|
|
}
|
|
|
|
// ── Noun phrase construction ──────────────────────────────────────────────────
|
|
//
|
|
// ja_noun_phrase: attach the correct case particle to a noun.
|
|
// The particle immediately follows the noun with no space.
|
|
|
|
fn ja_noun_phrase(noun: String, gram_case: String) -> String {
|
|
let p: String = ja_particle(gram_case)
|
|
if str_eq(p, "") {
|
|
return noun
|
|
}
|
|
return noun + p
|
|
}
|
|
|
|
// ── Question particle ─────────────────────────────────────────────────────────
|
|
//
|
|
// Japanese questions are formed by appending か (ka) to the end of a sentence.
|
|
|
|
fn ja_question_particle() -> String {
|
|
return "か"
|
|
}
|
|
|
|
// ── Sentence-final particle attachment ───────────────────────────────────────
|
|
//
|
|
// ja_make_question: append the question particle to a sentence.
|
|
|
|
fn ja_make_question(sentence: String) -> String {
|
|
return sentence + ja_question_particle()
|
|
}
|