From 1285476b4119de74911aa1e5764d5b0daf81aaf3 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Mon, 17 Aug 2026 01:22:52 +0900 Subject: [PATCH] =?UTF-8?q?korean:=20=E3=84=B3=20from=20two=20consonants,?= =?UTF-8?q?=20=EA=B0=80=20from=20a=20vowel=20typed=20first;=20index=20jamo?= =?UTF-8?q?=20by=20scanning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit libhangul, and so fcitx5-hangul, joins two lone consonants into the compound jong they make (rt → ㄳ, split again by a vowel: rtk → ㄱ사), and by default reorders a vowel typed before its consonant (kr → 가); strans committed the first jamo and started over. Backspace on a lone ㄳ leaves ㄱ, as on a syllable. The three jamo index tables and their -1 macros were the cho, jung, and jong arrays inverted by hand, bounded by a Jrange the callers had to respect; a linear scan of the arrays says the same in ten lines. --- ko.c | 79 +++++++++++++++++++++---------------------------- tests/ko_test.c | 7 +++++ 2 files changed, 40 insertions(+), 46 deletions(-) diff --git a/ko.c b/ko.c index e2e29a4..ec291e1 100644 --- a/ko.c +++ b/ko.c @@ -4,8 +4,6 @@ enum { Sbase = 0xAC00, - Jbase = 0x3131, - Jrange = 51, Ncho = 19, Njung = 21, Njong = 28, @@ -35,41 +33,21 @@ static Rune jong[] = { L'ㅌ', L'ㅍ', L'ㅎ', }; -static int choidx[Jrange] = { - [L'ㄱ'-Jbase] = 1, [L'ㄲ'-Jbase] = 2, [L'ㄴ'-Jbase] = 3, - [L'ㄷ'-Jbase] = 4, [L'ㄸ'-Jbase] = 5, [L'ㄹ'-Jbase] = 6, - [L'ㅁ'-Jbase] = 7, [L'ㅂ'-Jbase] = 8, [L'ㅃ'-Jbase] = 9, - [L'ㅅ'-Jbase] = 10, [L'ㅆ'-Jbase] = 11, [L'ㅇ'-Jbase] = 12, - [L'ㅈ'-Jbase] = 13, [L'ㅉ'-Jbase] = 14, [L'ㅊ'-Jbase] = 15, - [L'ㅋ'-Jbase] = 16, [L'ㅌ'-Jbase] = 17, [L'ㅍ'-Jbase] = 18, - [L'ㅎ'-Jbase] = 19, -}; +/* A jamo's index in cho, jung, or jong, or -1; jong[0] is no jong. */ +static int +idx(Rune *t, int n, Rune r) +{ + int i; -static int jungidx[Jrange] = { - [L'ㅏ'-Jbase] = 1, [L'ㅐ'-Jbase] = 2, [L'ㅑ'-Jbase] = 3, - [L'ㅒ'-Jbase] = 4, [L'ㅓ'-Jbase] = 5, [L'ㅔ'-Jbase] = 6, - [L'ㅕ'-Jbase] = 7, [L'ㅖ'-Jbase] = 8, [L'ㅗ'-Jbase] = 9, - [L'ㅘ'-Jbase] = 10, [L'ㅙ'-Jbase] = 11, [L'ㅚ'-Jbase] = 12, - [L'ㅛ'-Jbase] = 13, [L'ㅜ'-Jbase] = 14, [L'ㅝ'-Jbase] = 15, - [L'ㅞ'-Jbase] = 16, [L'ㅟ'-Jbase] = 17, [L'ㅠ'-Jbase] = 18, - [L'ㅡ'-Jbase] = 19, [L'ㅢ'-Jbase] = 20, [L'ㅣ'-Jbase] = 21, -}; + for(i = 0; i < n; i++) + if(t[i] == r) + return i; + return -1; +} -static int jongidx[Jrange] = { - [L'ㄱ'-Jbase] = 2, [L'ㄲ'-Jbase] = 3, [L'ㄳ'-Jbase] = 4, - [L'ㄴ'-Jbase] = 5, [L'ㄵ'-Jbase] = 6, [L'ㄶ'-Jbase] = 7, - [L'ㄷ'-Jbase] = 8, [L'ㄹ'-Jbase] = 9, [L'ㄺ'-Jbase] = 10, - [L'ㄻ'-Jbase] = 11, [L'ㄼ'-Jbase] = 12, [L'ㄽ'-Jbase] = 13, - [L'ㄾ'-Jbase] = 14, [L'ㄿ'-Jbase] = 15, [L'ㅀ'-Jbase] = 16, - [L'ㅁ'-Jbase] = 17, [L'ㅂ'-Jbase] = 18, [L'ㅄ'-Jbase] = 19, - [L'ㅅ'-Jbase] = 20, [L'ㅆ'-Jbase] = 21, [L'ㅇ'-Jbase] = 22, - [L'ㅈ'-Jbase] = 23, [L'ㅊ'-Jbase] = 24, [L'ㅋ'-Jbase] = 25, - [L'ㅌ'-Jbase] = 26, [L'ㅍ'-Jbase] = 27, [L'ㅎ'-Jbase] = 28, -}; - -#define Choidx(r) (choidx[(r) - Jbase] - 1) -#define Jungidx(r) (jungidx[(r) - Jbase] - 1) -#define Jongidx(r) (jongidx[(r) - Jbase] - 1) +#define Choidx(r) idx(cho, Ncho, r) +#define Jungidx(r) idx(jung, Njung, r) +#define Jongidx(r) idx(jong, Njong, r) static Rune jamomap[128] = { ['r'] = L'ㄱ', ['R'] = L'ㄲ', @@ -221,12 +199,23 @@ transko(Im *im, Rune c) sputr(&e.next, compose(ci, ji, 0)); return e; } - if(Jungidx(last) >= 0 && ji >= 0){ - comb = combine(cvow, nelem(cvow), last, jm); - if(comb){ - sputr(&e.next, comb); - return e; - } + /* A vowel typed first is reordered under its consonant. */ + if(Jungidx(last) >= 0 && Choidx(jm) >= 0){ + sputr(&e.next, compose(Choidx(jm), Jungidx(last), 0)); + return e; + } + /* Two vowels or two consonants may be one jamo, ㅘ or ㄳ. */ + comb = combine(cvow, nelem(cvow), last, jm); + if(comb == 0) + comb = combine(cjong, nelem(cjong), last, jm); + if(comb){ + sputr(&e.next, comb); + return e; + } + if(ji >= 0 && splitpair(cjong, nelem(cjong), last, &stay, &next)){ + sputr(&e.s, stay); + sputr(&e.next, compose(Choidx(next), ji, 0)); + return e; } e.s = im->pre; sputr(&e.next, jm); @@ -294,12 +283,10 @@ backko(Im *im) last = slastr(&im->pre); if(!issyl(last)){ - if(splitpair(cvow, nelem(cvow), last, &stay, &next)){ - spopr(&im->pre); - sputr(&im->pre, stay); - return; - } spopr(&im->pre); + if(splitpair(cvow, nelem(cvow), last, &stay, &next) || + splitpair(cjong, nelem(cjong), last, &stay, &next)) + sputr(&im->pre, stay); return; } decompose(last, &c, &j, &jo); diff --git a/tests/ko_test.c b/tests/ko_test.c index 01e9153..78d76fb 100644 --- a/tests/ko_test.c +++ b/tests/ko_test.c @@ -25,6 +25,12 @@ korean_sequences(struct ct *t) { "rr", "ㄱㄱ" }, { "Rk", "까" }, { "rk1", "가1" }, + { "rt", "ㄳ" }, + { "rtk", "ㄱ사" }, + { "rtt", "ㄳㅅ" }, + { "kr", "가" }, + { "hkr", "과" }, + { "kk", "ㅏㅏ" }, }; Str out; int i; @@ -81,6 +87,7 @@ korean_backspace(struct ct *t) { static const struct { char *before, *after; } cases[] = { { "ㄱ", "" }, + { "ㄳ", "ㄱ" }, { "가", "ㄱ" }, { "과", "고" }, { "괘", "고" }, { "괴", "고" }, { "궈", "구" }, { "궤", "구" }, { "귀", "구" },