skkeletonで使っている仮名入力ルーチン
割といい感じに動いてくれているので今後何か作る時のために残しておきたい
このコード自体はNYSLにしておくので好きに使ってほしい 前提として、仮名テーブルは入力文字列で辞書順にソートされたエントリのリストである
このようにしておくと入力でフィルタリングした際に、木を辿るかのように現在の結果と今後取り得る結果が手に入る
10万近くエントリがあるSKK辞書で同じことをしているが全く問題になっていないのでこれが問題になることは無いと思われる
木を作るコードを書いて保守するよりはいいと思う
skkeletonで使ってる物はかな入力考察を元に書いたものだがちょっと分かりづらいので書き直している 現在の入力が空の場合とそうでない場合に分けられ、それぞれのパターンに細かい差異があるのだが、同じ物を何度も書くのは面倒なので統合した
いずれのパターンでも入力とテーブルが完全にマッチする場合(残余が存在しない場合)は必ず確定可能
いずれのパターンでもテーブルに残余が出るケース(コードにバグが無い限り今までの入力が失敗しているとは考えられない)はfeedに積んで抜けられる
マッチしなかったパターンはそれぞれ処理が分かれる
現在の入力が空の場合は入力をそのまま確定する
テーブルに存在していない文字(例えば記号とか)なので打ち間違いとは考えられない、これはそのまま確定してほしい
kuuote.iconはこのへん勘違いしてバグらせた
現在の入力が存在した場合は、それが確定可能か調べる
確定可能であればそのまま確定する
確定不可能な場合、アルファベットのまま確定するか、あるいは破棄する(SKKのデフォルトはこの動き)
その上でもう一度最初から処理を繰り返す
ローマ字入力の場合はここの処理が肝心
ここがいい加減だと「んj」のようなパターンが処理できなくなる
ローマ字じゃなくても壊れたパターンを渡すと異常動作してしまう
これをTypeScriptに書き起こすとこうなる
書き直したバージョンはテストしてないのでバグってるかも
code:input.ts
import { acceptIllegalResult } from "./config.ts";
export type Entry = {
kana: string; // マッチする仮名
result: string; // 入力される結果
nextInput: string; // 確定した際に次の入力になる文字列、ほぼ「っ」用
};
export type State = {
feed: string; // 現在の入力
table: Entry[]; // テーブル。entry.kanaで辞書順にソートされている必要がある
result: string; // 入力結果のバッファ
};
function accept(state: State, entry: Entry) {
state.result += entry.result;
state.feed = entry.nextInput;
}
export function kanaInput(state: State, char: string) {
const next = state.feed + char;
const found = state.table.filter((e) => e.kana.startsWith(next));
// 次の入力とテーブルが正確にマッチした場合はそのまま確定
if (found.length === 1 && found0.kana === next) { } else if (found.length > 1) {
// テーブルに残余があったらfeedに入力を積む
state.feed = next;
} else if (state.feed.length !== 0) {
// テーブルとマッチせずfeedが存在した場合は
// feedを確定し、もう一度kanaInputに通す
// ローマ字入力の場合は「んj」などを正確に確定する必要があってちょっと面倒
const current = state.table.find((e) => e.kana === state.feed);
if (current != null) {
// feedが確定できる場合はそのまま確定する
accept(state, current);
} else if (acceptIllegalResult) {
state.result += state.feed;
}
state.feed = "";
kanaInput(state, char); // 円環の理
} else {
// feedが無い場合(=テーブルに存在しない文字)
// そのまま確定してしまう
state.result += char;
}
}