From 5faefd9a87fbbfa30007a9520b63db4f0fe7335c Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Mon, 17 Aug 2026 01:31:51 +0900 Subject: [PATCH] dict: lookups take a trie; a prefix search is its own function; no self-key rule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dictlookup took a Lang for two reasons that were not its business: to know whether to walk below the key (the emoji dictionary) and to drop a candidate equal to the key except there. No dictionary lists its key among its candidates — the rule was left from a first version that put the reading first itself — so it goes, and the walk is dictprefix(), called by the emoji search alone. --- dict.c | 46 ++++++++++++++++++++++++---------------------- fn.h | 3 ++- strans.c | 12 ++++++------ tests/dict_test.c | 45 +++++++++++++++++++++++++-------------------- tests/test.h | 2 +- tests/unit_test.c | 2 +- 6 files changed, 59 insertions(+), 51 deletions(-) diff --git a/dict.c b/dict.c index ddeb61e..ebf74e9 100644 --- a/dict.c +++ b/dict.c @@ -1,13 +1,9 @@ #include "dat.h" #include "fn.h" -/* - * Appends the space-separated words of a node's entry to out[], up to - * max: its candidates, minus the key itself, except in the emoji - * dictionary, where a query may name its own answer. - */ +/* Appends the space-separated words of a node's entry to out[], up to max. */ static int -words(Lang *l, Str *key, Tnode *nd, Str *out, int n, int max) +words(Tnode *nd, Str *out, int n, int max) { char *p, *e, *sp; Str tmp; @@ -22,8 +18,7 @@ words(Lang *l, Str *key, Tnode *nd, Str *out, int n, int max) sp = p; while(p < e && *p != ' ') p++; - if(sinit(&tmp, sp, p - sp) && tmp.n > 0 && - (l->lang == LangEMOJI || scmp(&tmp, key) != 0)) + if(sinit(&tmp, sp, p - sp) && tmp.n > 0) out[n++] = tmp; } return n; @@ -31,33 +26,40 @@ words(Lang *l, Str *key, Tnode *nd, Str *out, int n, int max) /* The entries at and below a node, in the dictionary's order. */ static int -below(Lang *l, Str *key, int ni, Str *out, int n, int max) +below(Trie *t, int ni, Str *out, int n, int max) { Tnode *nd; int ci; - nd = &l->dict->nodes[ni]; - n = words(l, key, nd, out, n, max); - for(ci = nd->child; ci >= 0 && n < max; ci = l->dict->nodes[ci].sibling) - n = below(l, key, ci, out, n, max); + nd = &t->nodes[ni]; + n = words(nd, out, n, max); + for(ci = nd->child; ci >= 0 && n < max; ci = t->nodes[ci].sibling) + n = below(t, ci, out, n, max); return n; } -/* - * Fills out[] with up to max candidates for key. The emoji dictionary is - * searched by prefix, the key's own entry first; the others exactly. - */ +/* Fills out[] with up to max candidates for key: the words of its entry. */ int -dictlookup(Lang *l, Str *key, Str *out, int max) +dictlookup(Trie *t, Str *key, Str *out, int max) { int ni; - ni = key->n == 0 ? -1 : trienode(l->dict, key); + ni = key->n == 0 ? -1 : trienode(t, key); if(ni < 0) return 0; - if(l->lang == LangEMOJI) - return below(l, key, ni, out, 0, max); - return words(l, key, &l->dict->nodes[ni], out, 0, max); + return words(&t->nodes[ni], out, 0, max); +} + +/* As dictlookup, for every entry key is a prefix of, key's own first. */ +int +dictprefix(Trie *t, Str *key, Str *out, int max) +{ + int ni; + + ni = key->n == 0 ? -1 : trienode(t, key); + if(ni < 0) + return 0; + return below(t, ni, out, 0, max); } static Trie* diff --git a/fn.h b/fn.h index 006241c..f49d523 100644 --- a/fn.h +++ b/fn.h @@ -21,7 +21,8 @@ int trielookup(Trie*, Str*, char**, int*); Lang* getlang(int); void langinit(char*); -int dictlookup(Lang*, Str*, Str*, int); +int dictlookup(Trie*, Str*, Str*, int); +int dictprefix(Trie*, Str*, Str*, int); void drawthread(void*); void popuparea(Area*, int, Area*, int, int, Area*); diff --git a/strans.c b/strans.c index ba956d8..31767bb 100644 --- a/strans.c +++ b/strans.c @@ -289,7 +289,7 @@ dictqokuri(Str *reading) key = *reading; key.n = i; sputr(&key, c); - n = dictlookup(im.l, &key, kouho, Maxkouho); + n = dictlookup(im.l->dict, &key, kouho, Maxkouho); sclear(&okuri); for(j = i - (reading->r[i-1] == L'っ'); j < reading->n; j++) sputr(&okuri, reading->r[j]); @@ -310,7 +310,7 @@ dictqjp(void) if(!isjp(&im) || !jpreading(im.l->map, &im.pre, &im.raw, &reading) || reading.n == 0) return; - im.nkouho = dictlookup(im.l, &reading, im.kouho, Maxkouho); + im.nkouho = dictlookup(im.l->dict, &reading, im.kouho, Maxkouho); dictqokuri(&reading); } @@ -572,13 +572,13 @@ emojiquery(void) transstr(im.l, nil, &search.raw, &local); foldascii(&local); clearkouho(); - n = dictlookup(getlang(LangEMOJI), &raw, kouho, Maxkouho); + n = dictprefix(getlang(LangEMOJI)->dict, &raw, kouho, Maxkouho); rawhit = n != 0; for(i = 0; i < n; i++) addkouho(&kouho[i]); localhit = 0; if(scmp(&raw, &local) != 0){ - n = dictlookup(getlang(LangEMOJI), &local, kouho, Maxkouho); + n = dictprefix(getlang(LangEMOJI)->dict, &local, kouho, Maxkouho); localhit = n != 0; for(i = 0; i < n; i++) addkouho(&kouho[i]); @@ -593,8 +593,8 @@ hanjaquery(void) { transstr(im.l, &search.seed, &search.raw, &search.text); clearkouho(); - im.nkouho = dictlookup(getlang(LangHANJA), &search.text, im.kouho, - Maxkouho); + im.nkouho = dictlookup(getlang(LangHANJA)->dict, &search.text, + im.kouho, Maxkouho); selectfirst(); } diff --git a/tests/dict_test.c b/tests/dict_test.c index 67dbb03..633617c 100644 --- a/tests/dict_test.c +++ b/tests/dict_test.c @@ -15,10 +15,10 @@ dictionary_candidates(struct ct *t) lang = getlang(LangJP); saved = lang->dict; lang->dict = trienew(); - trieput(lang->dict, "かな", strlen("かな"), " 候補1 かな 候補2 ", - strlen(" 候補1 かな 候補2 ")); + trieput(lang->dict, "かな", strlen("かな"), " 候補1 候補2 ", + strlen(" 候補1 候補2 ")); key = mkstr("かな"); - n = dictlookup(lang, &key, kouho, Maxkouho); + n = dictlookup(lang->dict, &key, kouho, Maxkouho); if(CT_EQ_INT(t, 2, n)){ checkstr(t, "candidate 1", "候補1", &kouho[0]); checkstr(t, "candidate 2", "候補2", &kouho[1]); @@ -34,13 +34,13 @@ dictionary_candidates(struct ct *t) *p = '\0'; trieput(lang->dict, "key", 3, many, strlen(many)); key = mkstr("key"); - n = dictlookup(lang, &key, kouho, Maxkouho); + n = dictlookup(lang->dict, &key, kouho, Maxkouho); if(CT_EQ_INT(t, Maxkouho, n)){ checkstr(t, "first capped candidate", "c00", &kouho[0]); snprint(item, sizeof item, "c%02d", Maxkouho-1); checkstr(t, "last capped candidate", item, &kouho[Maxkouho-1]); } - CT_EQ_INT(t, 3, dictlookup(lang, &key, kouho, 3)); + CT_EQ_INT(t, 3, dictlookup(lang->dict, &key, kouho, 3)); trieclose(lang->dict); lang->dict = saved; } @@ -56,28 +56,33 @@ dictionary_misses(struct ct *t) saved = lang->dict; lang->dict = trienew(); key = mkstr(""); - CT_EQ_INT(t, 0, dictlookup(lang, &key, kouho, Maxkouho)); + CT_EQ_INT(t, 0, dictlookup(lang->dict, &key, kouho, Maxkouho)); key = mkstr("missing"); - CT_EQ_INT(t, 0, dictlookup(lang, &key, kouho, Maxkouho)); - CT_EQ_INT(t, 0, dictlookup(getlang(LangKO), &key, kouho, Maxkouho)); + CT_EQ_INT(t, 0, dictlookup(lang->dict, &key, kouho, Maxkouho)); + CT_EQ_INT(t, 0, dictlookup(getlang(LangKO)->dict, &key, kouho, Maxkouho)); trieclose(lang->dict); lang->dict = saved; } void -dictionary_emoji_identity(struct ct *t) +dictionary_prefix(struct ct *t) { Str kouho[Maxkouho], key; - Trie *saved; - Lang *lang; + Trie *dict; - lang = getlang(LangEMOJI); - saved = lang->dict; - lang->dict = trienew(); - trieput(lang->dict, "é", strlen("é"), "é", strlen("é")); - key = mkstr("é"); - if(CT_EQ_INT(t, 1, dictlookup(lang, &key, kouho, Maxkouho))) - checkstr(t, "emoji identity candidate", "é", &kouho[0]); - trieclose(lang->dict); - lang->dict = saved; + dict = trienew(); + trieput(dict, "smile", 5, "A B", 3); + trieput(dict, "smiley", 6, "B C", 3); + trieput(dict, "sad", 3, "D", 1); + key = mkstr("smile"); + if(CT_EQ_INT(t, 4, dictprefix(dict, &key, kouho, Maxkouho))){ + checkstr(t, "own entry first", "A", &kouho[0]); + checkstr(t, "entry below", "C", &kouho[3]); + } + key = mkstr("s"); + CT_EQ_INT(t, 5, dictprefix(dict, &key, kouho, Maxkouho)); + CT_EQ_INT(t, 2, dictprefix(dict, &key, kouho, 2)); + key = mkstr("x"); + CT_EQ_INT(t, 0, dictprefix(dict, &key, kouho, Maxkouho)); + trieclose(dict); } diff --git a/tests/test.h b/tests/test.h index d9fe90a..9d20751 100644 --- a/tests/test.h +++ b/tests/test.h @@ -95,7 +95,7 @@ void engine_randomized_stress(struct ct*); void engine_full_boundary_passthrough(struct ct*); void dictionary_candidates(struct ct*); void dictionary_misses(struct ct*); -void dictionary_emoji_identity(struct ct*); +void dictionary_prefix(struct ct*); void ipc_masks_modifiers(struct ct*); void ipc_control_and_caret_frames(struct ct*); void ipc_runtime_path(struct ct*); diff --git a/tests/unit_test.c b/tests/unit_test.c index e094606..d800ab3 100644 --- a/tests/unit_test.c +++ b/tests/unit_test.c @@ -118,7 +118,7 @@ static const struct ct_test tests[] = { { "engine/full-boundary-passthrough", engine_full_boundary_passthrough }, { "dict/candidates", dictionary_candidates }, { "dict/misses", dictionary_misses }, - { "dict/emoji-identity", dictionary_emoji_identity }, + { "dict/prefix", dictionary_prefix }, { "ipc/masks-modifiers", ipc_masks_modifiers }, { "ipc/control-caret-frames", ipc_control_and_caret_frames }, { "ipc/runtime-path", ipc_runtime_path },