engine: verbs and adjectives through SKK's okuri-ari entries

A sixth of kanji.dict is SKK's okuri-ari entries, keyed by a stem and
the letter of the okurigana that follows it — かk: 書 描 掛 —, and no
reading ever ends in a letter, so かく offered 確 and 各 but never 書く,
and no verb or adjective could be converted at all.  Now every split of a
complete reading is tried, longest stem first, with the okurigana put
back: かく adds 書く and 描く after the exact candidates, よむ gives
読む, あかい 赤い, おおきい 大きい, いった 言った, かいた 書いた.  A stem
that ends in っ writes it in kana too, so いt and いっt agree.
This commit is contained in:
2026-08-17 01:18:09 +09:00
parent f61f7ab5f8
commit ab0366b7f7
2 changed files with 89 additions and 12 deletions

View File

@@ -110,6 +110,18 @@ cyclekouho(int delta)
im.sel = (im.sel + delta + im.nkouho) % im.nkouho; im.sel = (im.sel + delta + im.nkouho) % im.nkouho;
} }
static void
addkouho(Str *s)
{
int i;
for(i = 0; i < im.nkouho; i++)
if(scmp(&im.kouho[i], s) == 0)
return;
if(im.nkouho < Maxkouho)
im.kouho[im.nkouho++] = *s;
}
static int static int
numberedkouho(u32int ks, u32int mod) numberedkouho(u32int ks, u32int mod)
{ {
@@ -238,6 +250,56 @@ redraw(void)
channbsend(drawc, &dc); channbsend(drawc, &dc);
} }
/* SKK's letter for the okurigana a kana begins: its row, or its vowel. */
static Rune
okuriletter(Rune r)
{
static char *rows[] = {
"aあ", "iい", "uう", "eえ", "oお",
"kかきくけこ", "gがぎぐげご", "sさしすせそ", "zざじずぜぞ",
"tたちつてとっ", "dだぢづでど", "nなにぬねのん", "hはひふへほ",
"bばびぶべぼ", "pぱぴぷぺぽ", "mまみむめも", "yやゆよ",
"rらりるれろ", "wわ",
};
int i;
for(i = 0; i < nelem(rows); i++)
if(utfrune(rows[i]+1, r) != nil)
return rows[i][0];
return 0;
}
/*
* SKK keys a verb or adjective by its stem and the letter of the
* okurigana that follows (かk: 書, 描, ...). Every split of the reading
* is tried, longest stem first, and the okurigana is put back; a っ
* ending the stem (いっt: 言, 行) is written in kana too.
*/
static void
dictqokuri(Str *reading)
{
Str key, okuri, kouho[Maxkouho];
Rune c;
int i, j, n;
for(i = reading->n - 1; i > 0; i--){
c = okuriletter(reading->r[i]);
if(c == 0)
continue;
key = *reading;
key.n = i;
sputr(&key, c);
n = dictlookup(im.l, &key, kouho, Maxkouho);
sclear(&okuri);
for(j = i - (reading->r[i-1] == L''); j < reading->n; j++)
sputr(&okuri, reading->r[j]);
for(j = 0; j < n; j++){
sappend(&kouho[j], &okuri);
addkouho(&kouho[j]);
}
}
}
/* Kanji candidates for a complete reading; none is chosen yet. */ /* Kanji candidates for a complete reading; none is chosen yet. */
static void static void
dictqjp(void) dictqjp(void)
@@ -248,6 +310,7 @@ dictqjp(void)
if(!jpreading(im.l->map, &im.pre, &im.raw, &reading) || reading.n == 0) if(!jpreading(im.l->map, &im.pre, &im.raw, &reading) || reading.n == 0)
return; return;
im.nkouho = dictlookup(im.l, &reading, im.kouho, Maxkouho); im.nkouho = dictlookup(im.l, &reading, im.kouho, Maxkouho);
dictqokuri(&reading);
} }
static int static int
@@ -472,18 +535,6 @@ transstr(Lang *l, Str *raw, Str *out)
commitim(&q, out); commitim(&q, out);
} }
static void
addkouho(Str *s)
{
int i;
for(i = 0; i < im.nkouho; i++)
if(scmp(&im.kouho[i], s) == 0)
return;
if(im.nkouho < Maxkouho)
im.kouho[im.nkouho++] = *s;
}
/* The typed keys and their transliteration are both emoji queries. */ /* The typed keys and their transliteration are both emoji queries. */
static void static void
emojiquery(void) emojiquery(void)

View File

@@ -1200,6 +1200,32 @@ engine_japanese_candidates(struct ct *t)
checkstr(t, "typo mended", "かんじ", &shown); checkstr(t, "typo mended", "かんじ", &shown);
CT_EQ_INT(t, 2, im.nkouho); CT_EQ_INT(t, 2, im.nkouho);
CT_CHECK(t, keystroke(Kesc, 0, &com)); CT_CHECK(t, keystroke(Kesc, 0, &com));
/* SKK's okuri-ari entries convert verbs: stem plus okurigana. */
setdict(jp->dict, "かk", "書 描");
setdict(jp->dict, "かく", "");
if(!typekeys(t, "kaku", &com))
goto cleanup;
if(CT_EQ_INT(t, 3, im.nkouho)){
checkstr(t, "exact candidate first", "", &im.kouho[0]);
checkstr(t, "okurigana put back", "書く", &im.kouho[1]);
checkstr(t, "second okuri candidate", "描く", &im.kouho[2]);
}
CT_CHECK(t, keystroke(Kesc, 0, &com));
setdict(jp->dict, "おおk", "");
if(!typekeys(t, "ookii", &com))
goto cleanup;
if(CT_EQ_INT(t, 1, im.nkouho))
checkstr(t, "two-kana okurigana", "大きい", &im.kouho[0]);
CT_CHECK(t, keystroke(Kesc, 0, &com));
setdict(jp->dict, "いt", "言 行");
setdict(jp->dict, "いっt", "言 行");
if(!typekeys(t, "itta", &com))
goto cleanup;
if(CT_EQ_INT(t, 2, im.nkouho)){
checkstr(t, "sokuon written in kana", "言った", &im.kouho[0]);
checkstr(t, "sokuon written in kana", "行った", &im.kouho[1]);
}
CT_CHECK(t, keystroke(Kesc, 0, &com));
/* Backspace looks a complete shorter reading up again. */ /* Backspace looks a complete shorter reading up again. */
setdict(jp->dict, "かん", ""); setdict(jp->dict, "かん", "");
if(!typekeys(t, "kanji", &com)) if(!typekeys(t, "kanji", &com))