Compare commits

..

17 Commits

Author SHA1 Message Date
b4fdc6e813 optimize rendering 2026-02-09 09:24:50 +09:00
b22bc95ad5 add bench 2026-02-09 09:24:50 +09:00
d16b17b4c8 fix kouho selection. 2026-02-09 09:24:50 +09:00
d5d3c23f3e vietnamese telex input 2026-02-09 09:24:50 +09:00
032c60e193 add lang dispatch, korean hangul input 2026-02-09 09:24:45 +09:00
5653298bb1 use trie for map lookup 2026-02-08 16:36:59 +09:00
97f897755f rename hnode fields, functions. 2026-02-08 16:29:29 +09:00
O_WRONLY
f5f88e8451 update readme 2026-01-13 09:25:22 +09:00
db2b3eacad add vietnam telex input 2026-01-01 18:22:16 +09:00
559b82ec9b strans.c:Fix preedit window is not displayed or cleared. 2026-01-01 13:52:41 +09:00
O_WRONLY
9568893eaf Add Emoji input shortcut to README 2025-12-28 20:00:10 +09:00
874b941439 add emoji font.
fix: 97ae9b1709
2025-12-28 19:50:38 +09:00
72063f84b7 set default language to English
add dummy map/english.map
2025-12-28 17:33:50 +09:00
97ae9b1709 add eomji(ctrl+e)
TODO: Some emojis can't be displayed. A font fallback or font merge is required.
2025-12-28 16:49:26 +09:00
436a49c3ca remove jp.c 2025-12-28 13:02:53 +09:00
e5ce5fd61e rename line => pre 2025-12-28 12:26:28 +09:00
8a2ef65379 remove hangul.dict 2025-12-28 11:58:18 +09:00
34 changed files with 2399 additions and 12009 deletions

View File

@@ -1,12 +1,12 @@
CC = 9c CC = 9c
LD = 9l LD = 9l
CFLAGS = -Wall -Wextra -O2 CFLAGS = -Wall -Wextra -O2 -g
PROG = strans PROG = strans
SRCS = $(wildcard *.c) SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o) OBJS = $(SRCS:.c=.o)
all: $(PROG) all: $(PROG) xim bench
$(PROG): $(OBJS) $(PROG): $(OBJS)
$(LD) -o $@ $(OBJS) -lthread -lString -lbio -lxcb -lm $(LD) -o $@ $(OBJS) -lthread -lString -lbio -lxcb -lm
@@ -15,5 +15,13 @@ $(OBJS): dat.h fn.h ipc.h
clean: clean:
rm -f $(OBJS) $(PROG) rm -f $(OBJS) $(PROG)
make -C xim/ clean
make -C bench/ clean
.PHONY: all clean xim:
make -C xim/
bench:
make -C bench/
.PHONY: all clean xim bench

View File

@@ -1,18 +1,70 @@
# strans # strans
CJK input method
## Switch Key An input method daemon for CJK text entry on X11.
| Key | Action |
|-----|--------| Inspired by 9front's ktrans. Threads communicate via CSP channels.
| Ctrl+N | Hiragana |
| Ctrl+K | Katakana |
| Ctrl+S | Hangul |
| Ctrl+T | English |
## Dependencies ## Dependencies
- plan9port
- libxcb
## Reference - plan9port
- gtk+-3.0 (optional, for GTK IM module)
## Build
make
cd xim && make # XIM adapter
cd gtk && make docker && make install # GTK IM module
## Run
./strans map font &
For XIM apps:
./xim/strans-xim &
XMODIFIERS=@im=strans xterm
For GTK apps:
GTK_IM_MODULE=strans gedit
## Usage
Switch input modes with Ctrl + key:
N Hiragana
K Katakana
S Hangul
T English
V Vietnamese (Telex)
E Emoji
Type romanized input. Select candidates with 1-9 or arrow keys.
Tab or Enter to commit.
## Architecture
Four threads communicate via CSP channels:
- [imthread](strans.c#L271): keystroke processing, transliteration
- [dictthread](dict.c#L38): dictionary lookup
- [drawthread](win.c#L133): preedit window rendering
- [srvthread](srv.c#L42): IPC via unix socket
Adapters (strans-xim, im-strans.so) bridge X11/GTK events.
## Files
strans.c input method engine
dict.c dictionary queries
win.c xcb window management
font.c truetype rendering (stb_truetype)
map/ transliteration tables
font/ bundled CJK fonts
## References
- https://git.9front.org/plan9front/plan9front/HEAD/info.html - https://git.9front.org/plan9front/plan9front/HEAD/info.html
- https://np.mkv.li/kor/9front-ktrans-%ED%95%9C%EA%B8%80-%EB%98%91%EB%B0%94%EB%A1%9C-%EB%A7%8C%EB%93%A4%EA%B8%B0/ - https://np.mkv.li/kor/9front-ktrans-%ED%95%9C%EA%B8%80-%EB%98%91%EB%B0%94%EB%A1%9C-%EB%A7%8C%EB%93%A4%EA%B8%B0/
- https://en.wikipedia.org/wiki/Telex_(input_method)
- https://gist.github.com/hieuthi/0f5adb7d3f79e7fb67e0e499004bf558

23
bench.sh Executable file
View File

@@ -0,0 +1,23 @@
#!/bin/sh
pkill strans
pkill strans-xim
sleep 1
./strans map font &
sleep 1
# warm up glyph cache
./bench/bench bench/bench.keys 100
echo "cache warmed up"
STRANS_PID=$(pgrep -x strans)
perf record -g -o bench/perf.data -p "$STRANS_PID" &
PERF_PID=$!
sleep 1
./bench/bench bench/bench.keys 100
kill -INT $PERF_PID
wait $PERF_PID 2>/dev/null
pkill strans

8
bench/Makefile Normal file
View File

@@ -0,0 +1,8 @@
CC = cc
CFLAGS = -Wall -O2
bench: main.c
$(CC) $(CFLAGS) -o $@ main.c
clean:
rm -f bench

BIN
bench/bench Executable file

Binary file not shown.

30
bench/bench.keys Normal file
View File

@@ -0,0 +1,30 @@
^ntoukyounotenki^Ryoidesu^R
konpyuutanopuroguramu^B^B^Bgramu^R
nihongowobenkyoushiteimasu^R
sakuranoshanohana^B^B^B^Bhanagasaiteiru^R
kaigi^Ekyounokaigi^R
ashitahaiitenkininarudeshou^R
^sdkssudgktpdy^Rgksrmfekfqnxm^R
vmfhrrh^Btprtmxm^Rdufrlavldml^R
dmlrlwkekfwprtm^R
gksrnl^B^B^Bdlfp^R
answkd^Eanswkd^R
qkrtlwkf^B^B^Bwkfgkf^R
^thello world^R
the quick brown fox jumps over the lazy dog^R
programming is fun^R
^vVietNam^Rxinchaobancokhoekhong^R
toidilambaitap^B^B^Bbaitap^R
hoctiengviet^R
^nkaishanitsuutomeshiteimasu^R
tanaboraguukoudesu^B^B^B^B^Bkoudesu^R
^srhksrnrdlTkfgkwl^R
answkddkssud^B^Bdkssud^R
wnsdydgktpdy^R
^vtoidenlopcuahang^R
motngaymoiconduong^R
^thigh performance input method^R
^nnyuuryokuhouhou^R
tesutonosuuretsu^R
^sxkfldzmsdml^R
qhfmfjxm^R

168
bench/main.c Normal file
View File

@@ -0,0 +1,168 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/un.h>
enum {
Kback = 0xf008,
Kret = 0xf00d,
Kesc = 0xf01b,
Mctrl = 1<<2,
};
typedef struct Key Key;
struct Key {
int k;
int mod;
};
static Key *keys;
static int nkeys;
static int fd;
static void
die(char *msg)
{
perror(msg);
exit(1);
}
static void
addkey(int k, int mod)
{
static int cap;
if(nkeys >= cap){
cap = cap ? cap * 2 : 256;
keys = realloc(keys, cap * sizeof(Key));
if(!keys)
die("realloc");
}
keys[nkeys].k = k;
keys[nkeys].mod = mod;
nkeys++;
}
static void
loadkeys(char *file)
{
FILE *f;
int c;
f = fopen(file, "r");
if(!f)
die(file);
while((c = fgetc(f)) != EOF){
if(c == '\n' || c == '\r')
continue;
if(c != '^'){
addkey(c, 0);
continue;
}
c = fgetc(f);
if(c == EOF)
break;
switch(c){
case 'B':
addkey(Kback, 0);
break;
case 'R':
addkey(Kret, 0);
break;
case 'E':
addkey(Kesc, 0);
break;
default:
addkey(c, Mctrl);
break;
}
}
fclose(f);
}
static void
dial(void)
{
struct sockaddr_un addr;
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if(fd < 0)
die("socket");
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
snprintf(addr.sun_path, sizeof(addr.sun_path),
"/tmp/strans.%d", getuid());
if(connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0)
die("connect");
}
static void
sendkey(int key, int mod)
{
unsigned char req[4];
req[0] = 0;
req[1] = mod;
req[2] = key & 0xff;
req[3] = (key >> 8) & 0xff;
if(write(fd, req, 4) != 4)
die("write");
}
static int
readresp(void)
{
unsigned char buf[256];
int n;
if(read(fd, buf, 2) != 2)
return -1;
n = buf[1];
if(n > 0 && read(fd, buf + 2, n) != n)
return -1;
return 0;
}
static double
now(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec + ts.tv_nsec * 1e-9;
}
int
main(int argc, char **argv)
{
int i, j, niter;
double t0, t1, dt;
if(argc < 2){
fprintf(stderr, "usage: bench file [niter]\n");
exit(1);
}
loadkeys(argv[1]);
niter = argc > 2 ? atoi(argv[2]) : 1000;
dial();
t0 = now();
for(i = 0; i < niter; i++)
for(j = 0; j < nkeys; j++){
sendkey(keys[j].k, keys[j].mod);
if(readresp() < 0){
fprintf(stderr, "failed at iter %d key %d\n", i, j);
exit(1);
}
}
t1 = now();
dt = t1 - t0;
printf("%d iters x %d keys = %d keys\n", niter, nkeys, niter * nkeys);
printf("%.3f ms total, %.3f us/key, %.3f us/iter\n",
dt * 1000, dt * 1e6 / (niter * nkeys), dt * 1e6 / niter);
close(fd);
return 0;
}

BIN
bench/perf.data Normal file

Binary file not shown.

BIN
bench/perf.data.old Normal file

Binary file not shown.

42
dat.h
View File

@@ -13,10 +13,12 @@ enum
LangJP = 0x0e, LangJP = 0x0e,
LangJPK = 0x0b, LangJPK = 0x0b,
LangKO = 0x13, LangKO = 0x13,
LangEMOJI = 0x05,
LangVI = 0x16,
Fontsz = 32, Fontsz = 32,
Fontbase = 4, Fontbase = 4,
Nglyphs = 65536, Nglyphs = 0x20000,
}; };
enum enum
@@ -43,7 +45,6 @@ typedef struct Emit Emit;
struct Emit struct Emit
{ {
int eat; int eat;
int flush;
Str s; Str s;
Str next; Str next;
Str dict; Str dict;
@@ -56,8 +57,27 @@ struct Hnode
int next; int next;
char *key; char *key;
int klen; int klen;
char *kana; char *val;
int kanalen; int vlen;
};
typedef struct Tnode Tnode;
struct Tnode
{
int child;
int sibling;
char c;
char *val;
int vlen;
};
typedef struct Trie Trie;
struct Trie
{
int root;
Tnode *nodes;
int n;
int cap;
}; };
typedef struct Hmap Hmap; typedef struct Hmap Hmap;
@@ -77,15 +97,17 @@ struct Lang
int lang; int lang;
char *mapname; char *mapname;
char *dictname; char *dictname;
Hmap *map;
Hmap *dict;
Emit (*trans)(Im*, Rune); Emit (*trans)(Im*, Rune);
void (*back)(Im*);
void (*dictq)(Im*);
Trie *map;
Hmap *dict;
}; };
struct Im struct Im
{ {
Lang *l; Lang *l;
Str line; Str pre;
Str kouho[Maxkouho]; Str kouho[Maxkouho];
int nkouho; int nkouho;
int sel; int sel;
@@ -94,8 +116,8 @@ struct Im
typedef struct Drawcmd Drawcmd; typedef struct Drawcmd Drawcmd;
struct Drawcmd struct Drawcmd
{ {
Str preedit; Str pre;
Str kouho[Maxkouho]; Str kouho[Maxdisp];
int nkouho; int nkouho;
int sel; int sel;
}; };
@@ -112,7 +134,7 @@ typedef struct Dictreq Dictreq;
struct Dictreq struct Dictreq
{ {
Str key; Str key;
Str line; Str pre;
int lang; int lang;
}; };

16
dict.c
View File

@@ -2,7 +2,7 @@
#include "fn.h" #include "fn.h"
static void static void
dictlkup(Dictreq *req, Dictres *res) dictlookup(Dictreq *req, Dictres *res)
{ {
Lang *l; Lang *l;
Hmap *dict; Hmap *dict;
@@ -18,10 +18,10 @@ dictlkup(Dictreq *req, Dictres *res)
if(dict == nil) if(dict == nil)
return; return;
n = hmapget(dict, &req->key); n = hmapget(dict, &req->key);
if(n == nil || n->kanalen == 0) if(n == nil || n->vlen == 0)
return; return;
p = n->kana; p = n->val;
e = p + n->kanalen; e = p + n->vlen;
while(res->nkouho < Maxkouho && p < e){ while(res->nkouho < Maxkouho && p < e){
sp = p; sp = p;
while(p < e && *p != ' ') while(p < e && *p != ' ')
@@ -46,14 +46,14 @@ dictthread(void*)
break; break;
while(channbrecv(dictreqc, &req) > 0) while(channbrecv(dictreqc, &req) > 0)
; ;
dictlkup(&req, &res); dictlookup(&req, &res);
res.key = req.line; res.key = req.pre;
chansend(dictresc, &res); chansend(dictresc, &res);
} }
} }
static Hmap* static Hmap*
opendict(char *path) dictopen(char *path)
{ {
Hmap *h; Hmap *h;
Biobuf *b; Biobuf *b;
@@ -96,7 +96,7 @@ dictinit(char *dir)
if(langs[i].dictname == nil) if(langs[i].dictname == nil)
continue; continue;
snprint(path, sizeof(path), "%s/%s.dict", dir, langs[i].dictname); snprint(path, sizeof(path), "%s/%s.dict", dir, langs[i].dictname);
langs[i].dict = opendict(path); langs[i].dict = dictopen(path);
} }
} }

13
fn.h
View File

@@ -12,7 +12,11 @@ Rune slastr(Str*);
Hmap* hmapalloc(int, int); Hmap* hmapalloc(int, int);
int hmapset(Hmap**, Str*, Str*); int hmapset(Hmap**, Str*, Str*);
Hnode* hmapget(Hmap*, Str*); Hnode* hmapget(Hmap*, Str*);
int mapget(Hmap*, Str*, Str*); int mapget(Trie*, Str*, Str*);
Trie* trieopen(char*);
char* trieget(Trie*, char*, int, int*);
int trielookup(Trie*, char*, int, char**, int*);
Lang* getlang(int); Lang* getlang(int);
void mapinit(char*); void mapinit(char*);
@@ -22,8 +26,11 @@ void dictthread(void*);
void drawthread(void*); void drawthread(void*);
void imthread(void*); void imthread(void*);
Emit trans(Im*, Rune); Emit transmap(Im*, Rune);
Emit jptrans(Im*, Rune); Emit transko(Im*, Rune);
Emit transvi(Im*, Rune);
void backko(Im*);
void dictsend(Im*, Str*);
void srvthread(void*); void srvthread(void*);

92
font.c
View File

@@ -10,9 +10,12 @@ struct Glyph
int w, h, ox, oy; int w, h, ox, oy;
}; };
static stbtt_fontinfo font; enum { Maxfonts = 4 };
static uchar *fontdata;
static float scale; static stbtt_fontinfo fonts[Maxfonts];
static uchar *fontdata[Maxfonts];
static float scale[Maxfonts];
static int nfonts;
static Glyph cache[Nglyphs]; static Glyph cache[Nglyphs];
static u32int blendtab[2][256]; static u32int blendtab[2][256];
@@ -28,25 +31,64 @@ blend(u32int bg, int a)
return (r << 16) | (g << 8) | b; return (r << 16) | (g << 8) | b;
} }
void static void
fontinit(char *path) loadfont(char *path)
{ {
int fd, a; int fd;
long sz, n; long sz, n;
if(nfonts >= Maxfonts)
die("too many fonts");
fd = open(path, OREAD); fd = open(path, OREAD);
if(fd < 0) if(fd < 0)
die("can't open font: %s", path); die("can't open font: %s", path);
sz = seek(fd, 0, 2); sz = seek(fd, 0, 2);
seek(fd, 0, 0); seek(fd, 0, 0);
fontdata = emalloc(sz); fontdata[nfonts] = emalloc(sz);
n = readn(fd, fontdata, sz); n = readn(fd, fontdata[nfonts], sz);
close(fd); close(fd);
if(n != sz) if(n != sz)
die("can't read font: %s", path); die("can't read font: %s", path);
if(!stbtt_InitFont(&font, fontdata, stbtt_GetFontOffsetForIndex(fontdata, 0))) if(!stbtt_InitFont(&fonts[nfonts], fontdata[nfonts], stbtt_GetFontOffsetForIndex(fontdata[nfonts], 0)))
die("can't init font: %s", path); die("can't init font: %s", path);
scale = stbtt_ScaleForPixelHeight(&font, Fontsz); scale[nfonts] = stbtt_ScaleForPixelHeight(&fonts[nfonts], Fontsz);
nfonts++;
}
static int
isfont(char *name)
{
char *p;
p = strrchr(name, '.');
if(p == nil)
return 0;
return strcmp(p, ".ttf") == 0 || strcmp(p, ".otf") == 0;
}
void
fontinit(char *dir)
{
int fd, n, i, a;
Dir *d;
char path[256];
fd = open(dir, OREAD);
if(fd < 0)
die("can't open font dir: %s", dir);
n = dirreadall(fd, &d);
close(fd);
if(n < 0)
die("can't read font dir: %s", dir);
for(i = 0; i < n; i++){
if(isfont(d[i].name)){
snprint(path, sizeof path, "%s/%s", dir, d[i].name);
loadfont(path);
}
}
free(d);
if(nfonts == 0)
die("no fonts in %s", dir);
for(a = 0; a < 256; a++){ for(a = 0; a < 256; a++){
blendtab[0][a] = blend(Colbg, a); blendtab[0][a] = blend(Colbg, a);
blendtab[1][a] = blend(Colsel, a); blendtab[1][a] = blend(Colsel, a);
@@ -57,28 +99,36 @@ void
putfont(u32int *buf, int w, int h, int px, int py, Rune r) putfont(u32int *buf, int w, int h, int px, int py, Rune r)
{ {
Glyph *g; Glyph *g;
int i, j, x, y, a, sel; int i, j, a, sel, f;
int y0, j0, j1, x0, i0, i1;
u32int *p; u32int *p;
if(r >= Nglyphs) if(r >= Nglyphs)
return; return;
g = &cache[r]; g = &cache[r];
if(g->bmp == nil){ if(g->bmp == nil){
g->bmp = stbtt_GetCodepointBitmap(&font, scale, scale, r, &g->w, &g->h, &g->ox, &g->oy); for(f = 0; f < nfonts; f++){
if(stbtt_FindGlyphIndex(&fonts[f], r) == 0)
continue;
g->bmp = stbtt_GetCodepointBitmap(&fonts[f], scale[f], scale[f], r, &g->w, &g->h, &g->ox, &g->oy);
if(g->bmp != nil)
break;
}
if(g->bmp == nil) if(g->bmp == nil)
return; return;
} }
for(j = 0; j < g->h; j++){
y = py + j + g->oy + Fontsz - Fontbase; y0 = py + g->oy + Fontsz - Fontbase;
if(y < 0 || y >= h) j0 = y0 < 0 ? -y0 : 0;
continue; j1 = y0 + g->h > h ? h - y0 : g->h;
for(i = 0; i < g->w; i++){ x0 = px + g->ox;
x = px + i + g->ox; i0 = x0 < 0 ? -x0 : 0;
if(x < 0 || x >= w) i1 = x0 + g->w > w ? w - x0 : g->w;
continue; for(j = j0; j < j1; j++){
for(i = i0; i < i1; i++){
a = g->bmp[j * g->w + i]; a = g->bmp[j * g->w + i];
if(a > 0){ if(a > 0){
p = &buf[y * w + x]; p = &buf[(y0 + j) * w + x0 + i];
sel = (*p == Colsel) ? 1 : 0; sel = (*p == Colsel) ? 1 : 0;
*p = blendtab[sel][a]; *p = blendtab[sel][a];
} }

BIN
font/NotoEmoji-Regular.ttf Normal file

Binary file not shown.

BIN
font/NotoSans-Regular.ttf Normal file

Binary file not shown.

View File

@@ -1 +0,0 @@
NotoSansMonoCJKsc-Regular.otf

12
hash.c
View File

@@ -65,7 +65,7 @@ hmapget(Hmap *h, Str *key)
} }
static char* static char*
str2dup(Str *s) sdup(Str *s)
{ {
char buf[256]; char buf[256];
char *p; char *p;
@@ -79,7 +79,7 @@ str2dup(Str *s)
} }
int int
hmapset(Hmap **store, Str *key, Str *kana) hmapset(Hmap **store, Str *key, Str *val)
{ {
Hnode *n; Hnode *n;
uchar *v; uchar *v;
@@ -116,14 +116,14 @@ hmapset(Hmap **store, Str *key, Str *kana)
n = (Hnode*)v; n = (Hnode*)v;
replace: replace:
if(n->filled == 0){ if(n->filled == 0){
n->key = str2dup(key); n->key = sdup(key);
n->klen = strlen(n->key); n->klen = strlen(n->key);
n->filled = 1; n->filled = 1;
} }
n->next = next; n->next = next;
if(kana->n > 0){ if(val->n > 0){
n->kana = str2dup(kana); n->val = sdup(val);
n->kanalen = strlen(n->kana); n->vlen = strlen(n->val);
} }
return 0; return 0;
} }

80
jp.c
View File

@@ -1,80 +0,0 @@
#include "dat.h"
#include "fn.h"
Emit
jptrans(Im *im, Rune c)
{
Emit e;
Str key, kana;
Hmap *h;
Rune last;
int hira;
memset(&e, 0, sizeof(e));
h = im->l->map;
hira = (im->l->lang == LangJP);
key = im->line;
sputr(&key, c);
if(hmapget(h, &key)){
e.eat = 1;
e.next = key;
mapget(h, &key, &e.dict);
return e;
}
last = slastr(&im->line);
if(last == 0)
goto flush;
key = im->line;
key.n--;
if(mapget(h, &key, &kana)){
sclear(&key);
sputr(&key, last);
sputr(&key, c);
if(hmapget(h, &key)){
e.eat = 1;
e.s = kana;
sputr(&e.next, last);
sputr(&e.next, c);
mapget(h, &e.next, &e.dict);
return e;
}
}
if(c == 'n' && last == 'n'){
key = im->line;
key.n--;
if(mapget(h, &key, &kana)){
e.eat = 1;
e.s = kana;
sputr(&e.s, hira ? 0x3093 : 0x30F3);
sputr(&e.next, c);
mapget(h, &e.next, &e.dict);
return e;
}
}
if(c == last && strchr("kgsztdbpmjfchryw", c)){
key = im->line;
key.n--;
if(mapget(h, &key, &kana)){
e.eat = 1;
e.s = kana;
sputr(&e.s, hira ? 0x3063 : 0x30C3);
sputr(&e.next, c);
mapget(h, &e.next, &e.dict);
return e;
}
}
flush:
mapget(h, &im->line, &e.s);
sclear(&key);
sputr(&key, c);
if(hmapget(h, &key) == nil){
e.flush = 1;
sputr(&e.s, c);
return e;
}
e.eat = 1;
sputr(&e.next, c);
mapget(h, &e.next, &e.dict);
return e;
}

321
ko.c Normal file
View File

@@ -0,0 +1,321 @@
#include "dat.h"
#include "fn.h"
enum
{
Sbase = 0xAC00,
Jbase = 0x3131,
Jrange = 51,
Ncho = 19,
Njung = 21,
Njong = 28,
};
static Rune cho[] = {
L'', L'', L'', L'', L'',
L'', L'', L'', L'', L'',
L'', L'', L'', L'', L'',
L'', L'', L'', L'',
};
static Rune jung[] = {
L'', L'', L'', L'', L'',
L'', L'', L'', L'', L'',
L'', L'', L'', L'', L'',
L'', L'', L'', L'', L'',
L'',
};
static Rune jong[] = {
0, L'', L'', L'', L'',
L'', L'', L'', L'', L'',
L'', L'', L'', L'', L'',
L'', L'', L'', L'', L'',
L'', L'', L'', L'', L'',
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,
};
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,
};
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)
static Rune jamomap[128] = {
['r'] = L'', ['R'] = L'',
['s'] = L'', ['S'] = L'',
['e'] = L'', ['E'] = L'',
['f'] = L'', ['F'] = L'',
['a'] = L'', ['A'] = L'',
['q'] = L'', ['Q'] = L'',
['t'] = L'', ['T'] = L'',
['d'] = L'', ['D'] = L'',
['w'] = L'', ['W'] = L'',
['c'] = L'', ['C'] = L'',
['z'] = L'', ['Z'] = L'',
['x'] = L'', ['X'] = L'',
['v'] = L'', ['V'] = L'',
['g'] = L'', ['G'] = L'',
['k'] = L'', ['K'] = L'',
['o'] = L'', ['O'] = L'',
['i'] = L'', ['I'] = L'',
['j'] = L'', ['J'] = L'',
['p'] = L'', ['P'] = L'',
['u'] = L'', ['U'] = L'',
['h'] = L'', ['H'] = L'',
['y'] = L'', ['Y'] = L'',
['n'] = L'', ['N'] = L'',
['b'] = L'', ['B'] = L'',
['m'] = L'', ['M'] = L'',
['l'] = L'', ['L'] = L'',
};
typedef struct Cpair Cpair;
struct Cpair
{
Rune b;
Rune a;
Rune r;
};
static Cpair cvow[] = {
{L'', L'', L''},
{L'', L'', L''},
{L'', L'', L''},
{L'', L'', L''},
{L'', L'', L''},
{L'', L'', L''},
{L'', L'', L''},
};
static Cpair cjong[] = {
{L'', L'', L''},
{L'', L'', L''},
{L'', L'', L''},
{L'', L'', L''},
{L'', L'', L''},
{L'', L'', L''},
{L'', L'', L''},
{L'', L'', L''},
{L'', L'', L''},
{L'', L'', L''},
{L'', L'', L''},
};
static Rune
keytojamo(int c)
{
if(c >= 0 && c < 128)
return jamomap[c];
return 0;
}
static Rune
combine(Cpair *t, int n, Rune b, Rune a)
{
int i;
for(i = 0; i < n; i++)
if(t[i].b == b && t[i].a == a)
return t[i].r;
return 0;
}
static int
splitpair(Cpair *t, int n, Rune c, Rune *stay, Rune *next)
{
int i;
for(i = 0; i < n; i++){
if(t[i].r == c){
*stay = t[i].b;
*next = t[i].a;
return 1;
}
}
return 0;
}
static Rune
compose(int c, int j, int jo)
{
return Sbase + c*Njung*Njong + j*Njong + jo;
}
static void
decompose(Rune s, int *c, int *j, int *jo)
{
int i;
i = s - Sbase;
*c = i / (Njung * Njong);
*j = (i / Njong) % Njung;
*jo = i % Njong;
}
static int
issyl(Rune r)
{
return r >= Sbase && r < Sbase + Ncho*Njung*Njong;
}
Emit
transko(Im *im, Rune c)
{
Emit e;
Rune jm, jc, last, comb, stay, next;
int ci, ji, joi, ni, si, vi;
memset(&e, 0, sizeof e);
jm = keytojamo(c);
if(jm == 0){
if(im->pre.n > 0){
e.s = im->pre;
sclear(&im->pre);
}
sputr(&e.s, c);
return e;
}
e.eat = 1;
last = slastr(&im->pre);
if(last == 0){
sputr(&e.next, jm);
return e;
}
if(!issyl(last)){
ci = Choidx(last);
ji = Jungidx(jm);
if(ci >= 0 && ji >= 0){
spopr(&im->pre);
sputr(&e.next, compose(ci, ji, 0));
return e;
}
if(Jungidx(last) >= 0 && ji >= 0){
comb = combine(cvow, nelem(cvow), last, jm);
if(comb){
spopr(&im->pre);
sputr(&e.next, comb);
return e;
}
}
e.s = im->pre;
sputr(&e.next, jm);
return e;
}
decompose(last, &ci, &ji, &joi);
if(joi == 0){
ni = Jongidx(jm);
if(ni > 0){
spopr(&im->pre);
sputr(&e.next, compose(ci, ji, ni));
return e;
}
vi = Jungidx(jm);
if(vi >= 0){
comb = combine(cvow, nelem(cvow), jung[ji], jm);
if(comb){
ni = Jungidx(comb);
spopr(&im->pre);
sputr(&e.next, compose(ci, ni, 0));
return e;
}
}
}
if(joi > 0){
comb = combine(cjong, nelem(cjong), jong[joi], jm);
if(comb){
ni = Jongidx(comb);
if(ni > 0){
spopr(&im->pre);
sputr(&e.next, compose(ci, ji, ni));
return e;
}
}
vi = Jungidx(jm);
if(vi >= 0){
jc = jong[joi];
if(splitpair(cjong, nelem(cjong), jc, &stay, &next)){
si = Jongidx(stay);
ni = Choidx(next);
spopr(&im->pre);
sputr(&e.s, compose(ci, ji, si));
sputr(&e.next, compose(ni, vi, 0));
return e;
}
ni = Choidx(jc);
if(ni >= 0){
spopr(&im->pre);
sputr(&e.s, compose(ci, ji, 0));
sputr(&e.next, compose(ni, vi, 0));
return e;
}
}
}
e.s = im->pre;
sputr(&e.next, jm);
return e;
}
void
backko(Im *im)
{
Rune last;
int c, j, jo;
Rune stay, next;
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);
return;
}
decompose(last, &c, &j, &jo);
spopr(&im->pre);
if(jo > 0 && splitpair(cjong, nelem(cjong), jong[jo], &stay, &next)){
sputr(&im->pre, compose(c, j, Jongidx(stay)));
return;
}
if(jo > 0){
sputr(&im->pre, compose(c, j, 0));
return;
}
if(splitpair(cvow, nelem(cvow), jung[j], &stay, &next)){
sputr(&im->pre, compose(c, Jungidx(stay), 0));
return;
}
sputr(&im->pre, cho[c]);
}

8
main.c
View File

@@ -5,7 +5,7 @@ Channel *drawc;
Channel *keyc; Channel *keyc;
Channel *dictreqc; Channel *dictreqc;
Channel *dictresc; Channel *dictresc;
char *fontpath; char *fontdir;
int int
threadmaybackground(void) threadmaybackground(void)
@@ -16,7 +16,7 @@ threadmaybackground(void)
void void
usage(void) usage(void)
{ {
fprint(2, "usage: strans mapdir fontpath\n"); fprint(2, "usage: strans mapdir fontdir\n");
threadexitsall("usage"); threadexitsall("usage");
} }
@@ -60,8 +60,8 @@ threadmain(int argc, char **argv)
if(argc != 3) if(argc != 3)
usage(); usage();
fontpath = argv[2]; fontdir = argv[2];
drawc = chancreate(sizeof(Drawcmd), 0); drawc = chancreate(sizeof(Drawcmd), 4);
keyc = chancreate(sizeof(Keyreq), 0); keyc = chancreate(sizeof(Keyreq), 0);
dictreqc = chancreate(sizeof(Dictreq), 4); dictreqc = chancreate(sizeof(Dictreq), 4);
dictresc = chancreate(sizeof(Dictres), 0); dictresc = chancreate(sizeof(Dictres), 0);

179
map/emoji.dict Normal file
View File

@@ -0,0 +1,179 @@
! ⚠ ≠
!! ⚠
!= ≠
* ★
** ★
+ ±
+- ±
- →
-> →
. · … ÷
.. · …
... …
./ ÷
: ☹ ☺
:( ☹
:) ☺
< ← ≤ ≠ ♥
<- ←
<= ≤
<> ≠
= ≡ ⇒
== ≡
=> ⇒
> ≥
>= ≥
^ ⁽ ⁾ ⁺ ⁻ ⁼ ⁰ ¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ⁱ ⁿ
^( ⁽
^) ⁾
^+ ⁺
^- ⁻
^= ⁼
_ ₍ ₎ ₊ ₋ ₌ ₀ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ₐ ₑ ₒ ₓ
_( ₍
_) ₎
_+ ₊
_- ₋
_= ₌
~ ≈
~= ≈
^0 ⁰
_0 ₀
^1 ¹
_1 ₁
^2 ²
_2 ₂
<3 ♥
^3 ³
_3 ₃
^4 ⁴
_4 ₄
^5 ⁵
_5 ₅
^6 ⁶
_6 ₆
^7 ⁷
_7 ₇
^8 ⁸
_8 ₈
^9 ⁹
_9 ₉
_a ₐ
a α
al α
alp α
alph α
alpha α
b β
be β
bet β
beta β
c χ
ch χ
chi χ
d ° δ ↓
D Δ
de ° δ
De Δ
deg °
del δ
delt δ
delta δ
dn ↓
_e ₑ
e ε η
ep ε
eps ε
et η
eta η
g γ
G Γ
ga γ
Ga Γ
gam γ
gamm γ
gamma γ
^i ⁱ
i ∞ ι
I ∫
II ∫
in ∞
inf ∞
io ι
iot ι
iota ι
k κ
ka κ
kap κ
kapp κ
kappa κ
l λ
L Λ
la λ
La Λ
lam λ
lamb λ
lambd λ
lambda λ
m × μ
mu × μ
mul ×
^n ⁿ
n ν
nu ν
_o ₒ
o ω ●
O Ω
om ω
Om Ω
ome ω
omeg ω
omega ω
oo ●
p φ π ψ
P Φ Π ∏ Ψ
ph φ
Ph Φ
phi φ
pi π
Pi Π
PP ∏
ps ψ
Ps Ψ
psi ψ
r ρ
rh ρ
rho ρ
s σ
S Σ ∑
si σ
Si Σ
sig σ
sigm σ
sigma σ
sq √
SS ∑
t τ θ
T Θ
ta τ
tau τ
th θ
Th Θ
the θ
thet θ
theta θ
u ↑ υ
up ↑ υ
ups υ
v ✓
vv ✓
_x ₓ
x ξ ✗
X Ξ
xi ξ
Xi Ξ
xx ✗
z ζ
ze ζ
zet ζ
zeta ζ

179
map/emoji.map Normal file
View File

@@ -0,0 +1,179 @@
! !
!! ⚠
!= ≠
* *
** ★
+ +
+- ±
- -
-> →
. .
.. ·
... …
./ ÷
: :
:( ☹
:) ☺
< <
<- ←
<= ≤
<> ≠
= =
== ≡
=> ⇒
> >
>= ≥
^ ^
^( ⁽
^) ⁾
^+ ⁺
^- ⁻
^= ⁼
_ _
_( ₍
_) ₎
_+ ₊
_- ₋
_= ₌
~ ~
~= ≈
^0 ⁰
_0 ₀
^1 ¹
_1 ₁
^2 ²
_2 ₂
<3 ♥
^3 ³
_3 ₃
^4 ⁴
_4 ₄
^5 ⁵
_5 ₅
^6 ⁶
_6 ₆
^7 ⁷
_7 ₇
^8 ⁸
_8 ₈
^9 ⁹
_9 ₉
_a ₐ
a a
al al
alp alp
alph alph
alpha α
b b
be be
bet bet
beta β
c c
ch ch
chi χ
d d
D D
de de
De Δ
deg °
del del
delt delt
delta δ
dn ↓
_e ₑ
e e
ep ep
eps ε
et et
eta η
g g
G G
ga ga
Ga Γ
gam gam
gamm gamm
gamma γ
^i ⁱ
i i
I I
II ∫
in in
inf ∞
io io
iot iot
iota ι
k k
ka ka
kap kap
kapp kapp
kappa κ
l l
L L
la la
La Λ
lam lam
lamb lamb
lambd lambd
lambda λ
m m
mu μ
mul ×
^n ⁿ
n n
nu ν
_o ₒ
o o
O O
om om
Om Ω
ome ome
omeg omeg
omega ω
oo ●
p p
P P
ph ph
Ph Φ
phi φ
pi π
Pi Π
PP ∏
ps ps
Ps Ψ
psi ψ
r r
rh rh
rho ρ
s s
S S
si si
Si Σ
sig sig
sigm sigm
sigma σ
sq √
SS ∑
t t
T T
ta ta
tau τ
th th
Th Θ
the the
thet thet
theta θ
u u
up ↑
ups υ
v v
vv ✓
_x ₓ
x x
X X
xi ξ
Xi Ξ
xx ✗
z z
ze ze
zet zet
zeta ζ

99
map/emoji.src Normal file
View File

@@ -0,0 +1,99 @@
!! ⚠
!= ≠
** ★
+- ±
-> →
.. ·
... …
./ ÷
:( ☹
:) ☺
<- ←
<= ≤
<> ≠
== ≡
=> ⇒
>= ≥
^( ⁽
^) ⁾
^+ ⁺
^- ⁻
^= ⁼
_( ₍
_) ₎
_+ ₊
_- ₋
_= ₌
~= ≈
^0 ⁰
_0 ₀
^1 ¹
_1 ₁
^2 ²
_2 ₂
<3 ♥
^3 ³
_3 ₃
^4 ⁴
_4 ₄
^5 ⁵
_5 ₅
^6 ⁶
_6 ₆
^7 ⁷
_7 ₇
^8 ⁸
_8 ₈
^9 ⁹
_9 ₉
_a ₐ
alpha α
beta β
chi χ
deg °
delta δ
De Δ
dn ↓
_e ₑ
eps ε
eta η
gamma γ
Ga Γ
^i ⁱ
II ∫
inf ∞
iota ι
kappa κ
lambda λ
La Λ
mul ×
mu μ
^n ⁿ
nu ν
omega ω
Om Ω
oo ●
_o ₒ
phi φ
Ph Φ
pi π
Pi Π
PP ∏
psi ψ
Ps Ψ
rho ρ
sigma σ
Si Σ
sq √
SS ∑
tau τ
theta θ
Th Θ
up ↑
ups υ
vv ✓
xi ξ
Xi Ξ
xx ✗
_x ₓ
zeta ζ

0
map/english.map Normal file
View File

View File

@@ -1,382 +0,0 @@
;; Korean autocomplete dictionary for ktrans
;; Format: prefix completion1 completion2 ...
;;
;; Initial consonants (초성)
ㄱ 가 감사 감사합니다 강남 고마워 고맙습니다 괜찮아 그래 그럼 기다려 가능 가격 가게 가방 가족 간단 갈게 같이 거기 건강 검색 결과 경우 계속 고객 공부 관련 괜찮습니다 교육 구매 그냥 그런데 그리고 근데 금방 기능 기대 기본 길이
ㄴ 나 네 내일 너무 노래 누구 뭐 나중에 남자 내용 넣어 놓고 느낌 다음
ㄷ 다 대 더 도 동 다시 다음 단어 답변 대단 대화 도움 도착 동생 등록 드릴게요
ㄹ 라 로 리 레 뭐라고
ㅁ 만 먼저 메시지 모르겠어 모두 뭐 뭐야 뭐라고 뭔가 미안 미안해 문제 문의
ㅂ 바로 반갑습니다 방금 방법 별로 보내 보여줘 부탁 부탁드립니다 분명 비밀번호
ㅅ 사 사람 사랑 사용 사진 상관없어 생각 서비스 선택 설명 성공 소개 수고 수정 시간 시작 신청 실패 싶어
ㅇ 아 아니 아니요 아마 안녕 안녕하세요 알겠습니다 알았어 어디 어떻게 어서오세요 없어 에러 여기 여보세요 연락 열심히 예 오늘 오랜만 오케이 완료 왜 요청 우리 원래 위치 유저 으로 응 이거 이것 이름 이메일 이미 이상 이유 이해 인사 일단 입력 있어
ㅈ 자 잘 잘가 잘됐다 잠깐 잠시 저 전화 정말 제가 제발 조금 좀 좋아 좋겠다 주문 주세요 중요 즐거운 지금 직접 진짜 질문 집
ㅊ 참 참고 찾아 처리 처음 천만에요 최고 최근 추가 축하 출발 취소
ㅋ 카드 커피 컴퓨터 코드 클릭 큰
ㅌ 탈퇴 통화 특히
ㅍ 파일 편하게 포함 필요 필수
ㅎ 하 하나 하루 하세요 하지마 한 한번 항상 해결 해봐 해야 해주세요 핸드폰 행복 확인 회원 회의 휴대폰
;;
;; Common prefixes (일반 접두어)
가 가격 가게 가능 가다 가방 가수 가요 가을 가장 가족 가지
간 간단 간식 간장 간호사
갈 갈게 갈비 갈색
감 감기 감사 감사합니다 감사해요 감자 감정
강 강남 강아지 강의 강조
같 같아 같아요 같은 같이
개 개발 개발자 개선 개인 개정
거 거기 거래 거리 거의 거짓말
건 건강 건물 건의
걱 걱정 걱정마
검 검사 검색 검토
게 게시판 게임
결 결과 결론 결정 결제 결혼
경 경기 경험 경우 경제
계 계속 계시 계약 계정 계좌 계획
고 고객 고급 고기 고등학교 고마워 고맙습니다 고민 고양이 고장
곧 곧 곧바로
공 공간 공개 공부 공사 공유 공지 공항
과 과거 과연 과일 과장 과정
관 관계 관련 관리 관심
괜 괜찮아 괜찮아요 괜찮습니다
교 교사 교수 교육 교통 교환
구 구글 구독 구매 구분 구성 구입 구체적
국 국가 국내 국민 국어 국제
그 그거 그게 그것 그냥 그녀 그니까 그대 그대로 그때 그래 그래도 그래서 그러나 그러니까 그러면 그런 그런데 그럼 그렇게 그리고 그림 그만
근 근데 근무 근본 근처
글 글 글쎄 글자
금 금방 금액 금요일 금지
급 급하게 급해
기 기간 기능 기다려 기대 기본 기분 기사 기술 기억 기업 기준 기타 기회
길 길 길이
김 김밥 김치
까 까먹었어 까지
깜 깜빡 깜짝
꼭 꼭
끝 끝 끝났어 끝내
나 나 나도 나라 나머지 나빠 나오다 나와 나이 나중 나중에
날 날씨 날짜
남 남기다 남자 남편
내 내가 내년 내려 내리다 내용 내일
너 너 너무 너희
넣 넣다 넣어
네 네 네가 네이버
넷 넷플릭스
노 노래 노력 노트북
놀 놀라 놀러
농 농담
높 높은 높이
누 누가 누구 누나
눈 눈 눈물
뉴 뉴스
느 느낌 느린
늦 늦게 늦었어
다 다 다녀오다 다니다 다른 다르다 다시 다양 다음 다행 다행이다
단 단계 단순 단어 단위 단점 단체
달 달 달라 달러 달리
담 담당 담당자 담배
답 답글 답변 답장
당 당근 당분간 당신 당연 당장
대 대기 대단 대략 대부분 대신 대체 대충 대표 대학 대학교 대화 대해
더 더 더욱
덕 덕분
데 데이터
도 도대체 도서관 도시 도와줘 도움 도착 도전
독 독서
돈 돈
동 동네 동생 동시 동안 동영상 동의
돼 돼 돼요 됐어
되 되게 되다 되면
두 두 두고 두번째
뒤 뒤 뒤에
드 드디어 드라마 드리다 드릴게요 드세요
등 등록 등등
따 따라 따로
딱 딱
때 때 때문 때문에
또 또 또는 또한
똑 똑같이
뛰 뛰어
라 라면 라이브
로 로그인 로그아웃
리 리뷰
마 마다 마셔 마시다 마음 마지막 마찬가지 마침
막 막 막상
만 만 만나 만나다 만나서 반가워요 만들다 만약 만원 만큼 만족
많 많다 많아 많이
말 말 말고 말씀 말야 말이야
맛 맛 맛있어 맛있다
망 망했다
맞 맞아 맞아요 맞춤
매 매번 매우 매일 매장
머 머리
먹 먹고 먹다 먹어
먼 먼저
멀 멀다 멀리
메 메뉴 메모 메시지 메일
며 며칠
면 면접
명 명 명단 명확
몇 몇 몇개 몇번
모 모두 모든 모르겠어 모르다 모레 모양 모임
목 목록 목요일 목적
몰 몰라 몰랐어
못 못 못하다 못해
무 무료 무슨 무엇 무조건
문 문서 문의 문자 문제
물 물 물건 물론 물어보다
뭐 뭐 뭐야 뭐라고 뭔가 뭘
미 미국 미래 미리 미안 미안해 미안합니다 미팅
믿 믿다 믿어
밑 밑에
바 바꾸다 바뀌다 바라다 바로 바쁘다 바쁘면
박 박사
반 반 반갑습니다 반드시 반복
받 받다 받아 받았어
발 발생 발전 발표
밤 밤
밥 밥
방 방금 방법 방문 방송 방식 방향
배 배 배달 배송 배우다
백 백 백만
버 버그 버리다 버전
번 번 번역 번호
벌 벌써
범 범위
법 법
별 별로
변 변경 변화
병 병원
보 보고 보고서 보내다 보다 보면 보여줘 보이다 보통 보호
복 복사 복잡
본 본인
봐 봐 봐요 봐줘
부 부끄러워 부디 부모님 부분 부장 부족 부탁 부탁드립니다 부탁해
분 분 분명 분석 분위기
불 불가능 불편 불행
비 비교 비밀 비밀번호 비슷 비용
빠 빠르게 빠르다 빠른 빨리
빼 빼다 빼고
사 사고 사람 사랑 사랑해 사무실 사실 사업 사용 사용자 사이 사이트 사장 사전 사진 사회
산 산
살 살다 살펴보다
상 상관 상관없어 상담 상대 상세 상태 상품 상황
새 새로 새로운 새벽
색 색 색상
생 생각 생각해 생기다 생산 생일 생활
서 서로 서류 서버 서비스 서울
선 선물 선배 선생님 선택
설 설명 설정 설치
성 성공 성격 성능 성함
세 세계 세금 세부 세상 세요
소 소개 소리 소비자 소식 소중 소프트웨어
속 속도
손 손 손님
솔 솔직히
송 송금
쇼 쇼핑
수 수 수고 수고하세요 수량 수업 수요일 수정 수준 수출
숙 숙제
순 순서
술 술
쉬 쉬다 쉬운 쉽게
스 스마트폰 스케줄 스트레스 스타일
슬 슬프다
시 시 시간 시스템 시작 시장 시험
식 식당 식사
신 신경 신고 신용 신제품 신청 신호 신뢰 신규
실 실례 실수 실제 실패 실행 실험 실화
심 심각 심심
싶 싶다 싶어 싶으면
싸 싸다 싸움
쓰 쓰다 쓰레기 쓸
씨 씨
아 아 아까 아끼다 아니 아니요 아님 아마 아무 아버지 아이 아이디 아직 아침 아파 아프다
안 안 안내 안녕 안녕하세요 안되다 안전
알 알겠습니다 알겠어 알다 알려줘 알림 알바 알았어
앞 앞으로 앞에
애 애인
야 야
약 약 약간 약속
얘 얘기
어 어 어느 어디 어떤 어떻게 어렵다 어머니 어서 어제 어쩌면 어차피 어플
언 언니 언제 언젠가
얼 얼굴 얼마 얼마나
업 업그레이드 업데이트 업로드 업무 업체
없 없다 없어 없으면
에 에러
여 여기 여러 여름 여보세요 여자 여전히 여행
역 역시
연 연결 연구 연락 연습 연휴
열 열다 열심히
영 영상 영수증 영어 영업 영향 영화
예 예 예를 들어 예상 예약 예전 예정
오 오늘 오다 오래 오랜만 오른쪽 오빠 오전 오케이 오후
올 올라가다 올려 올리다 올해
와 와 와이파이 왔어
완 완료 완벽 완전
왜 왜
외 외국
요 요금 요새 요즘 요청
욕 욕
용 용 용도
우 우리 우선 우연히
운 운동 운영 운전
울 울다 울어
원 원 원래 원인 원하다 원해
월 월 월급 월요일
웹 웹사이트
위 위 위에 위주 위치 위해
유 유명 유용 유저 유지 유튜브 유행
으 으로 으면
은 은 은행
을 을
음 음 음식 음악
응 응 응답
의 의견 의미 의문 의사
이 이 이거 이것 이곳 이기다 이따 이때 이래 이런 이렇게 이름 이리 이메일 이미 이번 이상 이야기 이용 이유 이제 이전 이쪽 이틀 이해 이해하다
인 인간 인기 인사 인스타그램 인정 인증 인터넷 인터뷰
일 일 일단 일반 일부 일상 일요일 일을 일자 일정 일찍 일하다
읽 읽다 읽어
잃 잃다 잃어버리다
입 입금 입력 입장 입학
있 있다 있어 있으면
자 자 자기 자꾸 자다 자동 자료 자세히 자신 자연 자유 자주
작 작년 작동 작성 작업 작은 작품
잔 잔
잘 잘 잘가 잘됐다 잘못 잘하다
잠 잠 잠깐 잠시
장 장 장기 장난 장소 장점
잡 잡다 잡아
재 재미 재밌다 재밌어
저 저 저거 저기 저녁 저도 저렇게 저번 저장 저희
적 적다 적용 적절 적어도
전 전 전공 전달 전문 전부 전에 전자 전체 전혀 전화
절 절대 절반
점 점 점검 점심 점점
접 접속 접수 접근
정 정기 정도 정리 정말 정보 정상 정신 정식 정확
제 제가 제공 제대로 제목 제발 제안 제외 제일 제품
젤 젤
조 조건 조금 조사 조심 조절 조용 조치
존 존경 존재 존중
졸 졸려
종 종류 종종
좀 좀
좋 좋겠다 좋다 좋아 좋아요 좋아하다 좋은
죄 죄송 죄송합니다
주 주 주간 주로 주말 주문 주변 주세요 주소 주요 주의 주인 주제
준 준비
줄 줄 줄게
중 중간 중국 중심 중에 중요
즉 즉시
즐 즐거운 즐겁다 즐기다
증 증가
지 지각 지금 지나다 지난 지루 지역 지원 지인 지정 지출
직 직업 직원 직장 직접
진 진심 진짜 진행
질 질문 질서
집 집 집중
짧 짧은
짧 짧다
쪽 쪽 쪽으로
찍 찍다 찍어
차 차 차이 차후
참 참 참고 참여 참으로
찾 찾고 찾다 찾아 찾으면
채 채널 채팅
책 책 책임
처 처리 처음
천 천만에요 천원
철 철저
첫 첫 첫번째
청 청소 청구
체 체크 체험
초 초 초대 초반 초보
최 최고 최근 최대 최소 최선 최신 최적 최종
추 추가 추석 추천 추후
축 축하 축하드립니다 축하합니다 축하해
출 출근 출력 출발 출시 출장
충 충분 충전
취 취미 취소 취업 취향
치 치킨
친 친구 친절 친척
카 카드 카메라 카카오 카카오톡 카페
칼 칼퇴
커 커뮤니티 커피 커피숍
컨 컨디션 컨텐츠
컴 컴퓨터
케 케이스
코 코드 코로나
콘 콘서트 콘텐츠
클 클래스 클라이언트 클릭
큰 큰 큰일
타 타다 타이밍
탈 탈퇴
테 테스트 테이블
토 토요일
통 통신 통역 통장 통화
투 투자
특 특별 특별히 특이 특징 특히
틀 틀리다 틀림없이
티 티켓 티비
파 파악 파일 파티
판 판단 판매
팔 팔다 팔로우
팀 팀 팀장
패 패스워드
편 편리 편안 편의 편지 편하게 편하다
평 평가 평균 평소 평일
폰 폰
표 표 표시 표현
품 품질
프 프로그램 프로젝트 프로필
플 플랜
피 피곤 피드백 피하다
필 필수 필요 필요하다
하 하 하고 하나 하는 하다 하루 하면 하세요 하지마 하지만
한 한 한국 한글 한달 한두 한번 한편 한테
할 할 할게 할인
함 함께 함수
합 합계 합니다 합의
항 항목 항상
해 해 해결 해도 해봐 해볼게 해서 해야 해요 해외 해주세요
핵 핵심
행 행동 행복 행사
향 향상
허 허락
현 현금 현상 현재 현황
혹 혹시
홈 홈페이지
화 화 화나다 화요일 화이팅 화인 화장실
확 확대 확보 확실 확실히 확인
환 환경 환불 환영
활 활동 활용
회 회 회사 회식 회원 회의
효 효과
후 후 후기 후에
휴 휴가 휴대폰 휴식 휴일
흔 흔히
힘 힘 힘내 힘들다 힘들어
;;
;; Common phrases (자주 쓰는 문장)
감사합 감사합니다
고맙습 고맙습니다
고마워 고마워요
괜찮습 괜찮습니다
반갑습 반갑습니다
안녕하 안녕하세요 안녕하십니까
알겠습 알겠습니다
죄송합 죄송합니다
축하합 축하합니다
수고하 수고하세요 수고하셨습니다
미안합 미안합니다
실례합 실례합니다
부탁드 부탁드립니다
감사해 감사해요
고마워 고마워요
미안해 미안해요
안녕히 안녕히 가세요 안녕히 계세요
잘부탁 잘부탁드립니다
오랜만 오랜만이에요 오랜만입니다
좋은하 좋은하루

File diff suppressed because it is too large Load Diff

39
map/mkemoji Executable file
View File

@@ -0,0 +1,39 @@
#!/bin/sh
awk '
BEGIN { FS = "\t" }
/^$/ { next }
{
key = $1
val = $2
map[key] = val
for(i = 1; i < length(key); i++){
p = substr(key, 1, i)
if(!(p in map))
map[p] = p
}
}
END {
for(k in map)
print k "\t" map[k]
}
' $1 | sort -k1,1 > emoji.map
awk '
BEGIN { FS = "\t" }
/^$/ { next }
{
key = $1
val = $2
for(i = 1; i <= length(key); i++){
p = substr(key, 1, i)
dict[p] = dict[p] " " val
}
}
END {
for(p in dict){
sub(/^ /, "", dict[p])
print p "\t" dict[p]
}
}
' $1 | sort -k1,1 > emoji.dict

159
map/mktelex.py Executable file
View File

@@ -0,0 +1,159 @@
#!/usr/bin/env python3
# tone marks: s=rising f=falling r=hook x=tilde j=dot
tone = {
's': str.maketrans("aăâeêioôơuưy", "áắấéếíóốớúứý"),
'f': str.maketrans("aăâeêioôơuưy", "àằầèềìòồờùừỳ"),
'r': str.maketrans("aăâeêioôơuưy", "ảẳẩẻểỉỏổởủửỷ"),
'x': str.maketrans("aăâeêioôơuưy", "ãẵẫẽễĩõỗỡũữỹ"),
'j': str.maketrans("aăâeêioôơuưy", "ạặậẹệịọộợụựỵ"),
}
# modified vowels: input -> output
modvowel = [
("aw", "ă"),
("aa", "â"),
("ee", "ê"),
("oo", "ô"),
("ow", "ơ"),
("uw", "ư"),
]
# modified cons: input -> output
modcons = [
("dd", "đ"),
]
upper = str.maketrans(
"aăâeêioôơuưyđáắấéếíóốớúứýàằầèềìòồờùừỳảẳẩẻểỉỏổởủửỷãẵẫẽễĩõỗỡũữỹạặậẹệịọộợụựỵ",
"AĂÂEÊIOÔƠUƯYĐÁẮẤÉẾÍÓỐỚÚỨÝÀẰẦÈỀÌÒỒỜÙỪỲẢẲẨẺỂỈỎỔỞỦỬỶÃẴẪẼỄĨÕỖỠŨỮỸẠẶẬẸỆỊỌỘỢỤỰỴ")
def addtone(v, t):
return v.translate(tone[t])
def emit(input, output):
print(f"{input}\t{output}")
def up(s):
c = s[0].translate(upper)
if c == s[0]:
c = s[0].upper()
return c + s[1:]
print(f"{up(input)}\t{up(output)}")
def vowel1():
for v in "aeiouy":
emit(v, v)
for t in tone:
emit(v+t, addtone(v, t))
def vowel2():
# input, output, vowel
tab = [
("oa", "oa", "a"), ("oe", "oe", "e"), ("ai", "ai", "a"),
("ao", "ao", "a"), ("au", "au", "a"), ("ay", "ay", "a"),
("eu", "eu", "e"), ("iu", "iu", "i"), ("oi", "oi", "o"),
("ui", "ui", "u"), ("uy", "uy", "y"),
("iee", "", "ê"), ("yee", "", "ê"), ("uoo", "", "ô"),
("uow", "ươ", "ơ"), ("uaa", "", "â"), ("oaw", "", "ă"),
("uwa", "ưa", "a"), ("uwow", "ươ", "ơ"),
]
for i, o, v in tab:
emit(i, o)
for t in tone:
emit(i+t, o.replace(v, addtone(v, t), 1))
emit("ie", "ie")
emit("ye", "ye")
emit("uo", "uo")
emit("ua", "ua")
def vowel3():
# input, output, vowel
tab = [
("ieeu", "iêu", "ê"), ("yeeu", "yêu", "ê"),
("uooi", "uôi", "ô"), ("uowi", "ươi", "ơ"),
("oai", "oai", "a"), ("oay", "oay", "a"),
("uyee", "uyê", "ê"),
]
for i, o, v in tab:
emit(i, o)
for t in tone:
emit(i+t, o.replace(v, addtone(v, t), 1))
emit("uya", "uya")
def modvowels():
for i, o in modvowel:
emit(i, o)
def modconss():
for i, o in modcons:
emit(i, o)
def mod1tone():
# aw+s -> ắ
for i, o in modvowel:
for t in tone:
emit(i+t, addtone(o, t))
modvowel = [
("aw", "ă"),
("aa", "â"),
("ee", "ê"),
("oo", "ô"),
("ow", "ơ"),
("uw", "ư"),
]
def tone1mod():
# a+s+w -> ắ
for i, o in modvowel:
for t in tone:
emit(i[0]+t+i[1], addtone(o, t))
def tone2mod():
# ie+s+e -> iế
# input, output, vowel, suffix
tab = [
("ie", "", "ê", "e"), ("ye", "", "ê", "e"),
("uo", "", "ô", "o"), ("uo", "ươ", "ơ", "w"),
("ua", "", "â", "a"), ("oa", "", "ă", "w"),
("uwo", "ươ", "ơ", "w"),
]
for i, o, v, s in tab:
for t in tone:
emit(i+t+s, o.replace(v, addtone(v, t), 1))
def escape():
# aww -> aw
for e in ["aw", "aa", "ee", "oo", "ow", "uw", "dd"]:
emit(e+e[-1], e)
def final():
# codas
coda = ["c", "m", "n", "p", "t", "ch", "ng", "nh"]
# input, output, vowel
tab = [
("a", "a", "a"), ("e", "e", "e"), ("i", "i", "i"),
("o", "o", "o"), ("u", "u", "u"), ("y", "y", "y"),
("aw", "ă", "ă"), ("aa", "â", "â"), ("ee", "ê", "ê"),
("oo", "ô", "ô"), ("ow", "ơ", "ơ"), ("uw", "ư", "ư"),
("iee", "", "ê"), ("yee", "", "ê"), ("uoo", "", "ô"),
("uow", "ươ", "ơ"), ("uaa", "", "â"), ("oaw", "", "ă"),
("ai", "ai", "a"), ("ao", "ao", "a"), ("au", "au", "a"),
("ay", "ay", "a"), ("oa", "oa", "a"), ("oi", "oi", "o"),
("ui", "ui", "u"), ("uy", "uy", "y"),
]
for i, o, v in tab:
for c in coda:
emit(i+c, o+c)
for t in tone:
emit(i+c+t, o.replace(v, addtone(v, t), 1)+c)
vowel1()
vowel2()
vowel3()
modvowels()
modconss()
mod1tone()
tone1mod()
tone2mod()
escape()
final()

View File

@@ -1,131 +1,589 @@
aw ă a a
A A
aa â aa â
Aa Â
aaa aa
Aaa Aa
aac âc
Aac Âc
aach âch
Aach Âch
aam âm
Aam Âm
aan ân
Aan Ân
aang âng
Aang Âng
aanh ânh
Aanh Ânh
aap âp
Aap Âp
aat ât
Aat Ât
ac ac
Ac Ac
ach ach
Ach Ach
ai ai
Ai Ai
aic aic
Aic Aic
aich aich
Aich Aich
aim aim
Aim Aim
ain ain
Ain Ain
aing aing
Aing Aing
ainh ainh
Ainh Ainh
aip aip
Aip Aip
ait ait
Ait Ait
am am
Am Am
an an
An An
ang ang
Ang Ang
anh anh
Anh Anh
ao ao
Ao Ao
aoc aoc
Aoc Aoc
aoch aoch
Aoch Aoch
aom aom
Aom Aom
aon aon
Aon Aon
aong aong
Aong Aong
aonh aonh
Aonh Aonh
aop aop
Aop Aop
aot aot
Aot Aot
ap ap
Ap Ap
at at
At At
au au
Au Au
auc auc
Auc Auc
auch auch
Auch Auch
aum aum
Aum Aum
aun aun
Aun Aun
aung aung
Aung Aung
aunh aunh
Aunh Aunh
aup aup
Aup Aup
aut aut
Aut Aut
aw ă
Aw Ă
awc ăc
Awc Ăc
awch ăch
Awch Ăch
awm ăm
Awm Ăm
awn ăn
Awn Ăn
awng ăng
Awng Ăng
awnh ănh
Awnh Ănh
awp ăp
Awp Ăp
awt ăt
Awt Ăt
aww aw
Aww Aw
ay ay
Ay Ay
ayc ayc
Ayc Ayc
aych aych
Aych Aych
aym aym
Aym Aym
ayn ayn
Ayn Ayn
ayng ayng
Ayng Ayng
aynh aynh
Aynh Aynh
ayp ayp
Ayp Ayp
ayt ayt
Ayt Ayt
dd đ dd đ
Dd Đ
ddd dd
Ddd Dd
ec ec
Ec Ec
ech ech
Ech Ech
e e
E E
eec êc
Eec Êc
eech êch
Eech Êch
ee ê ee ê
Ee Ê
eee ee
Eee Ee
eem êm
Eem Êm
een ên
Een Ên
eeng êng
Eeng Êng
eenh ênh
Eenh Ênh
eep êp
Eep Êp
eet êt
Eet Êt
em em
Em Em
en en
En En
eng eng
Eng Eng
enh enh
Enh Enh
ep ep
Ep Ep
et et
Et Et
eu eu
Eu Eu
ich ich
Ich Ich
ic ic
Ic Ic
ieech iêch
Ieech Iêch
ieec iêc
Ieec Iêc
iee iê
Iee Iê
ieem iêm
Ieem Iêm
ieeng iêng
Ieeng Iêng
ieenh iênh
Ieenh Iênh
ieen iên
Ieen Iên
ieep iêp
Ieep Iêp
ieet iêt
Ieet Iêt
ieeu iêu
Ieeu Iêu
iefe iề
Iefe Iề
ie ie
Ie Ie
ieje iệ
Ieje Iệ
iere iể
Iere Iể
iese iế
Iese Iế
iexe iễ
Iexe Iễ
i i
I I
im im
Im Im
ing ing
Ing Ing
inh inh
Inh Inh
in in
In In
ip ip
Ip Ip
it it
It It
iu iu
Iu Iu
oach oach
Oach Oach
oac oac
Oac Oac
oafw oằ
Oafw Oằ
oai oai
Oai Oai
oajw oặ
Oajw Oặ
oam oam
Oam Oam
oang oang
Oang Oang
oanh oanh
Oanh Oanh
oan oan
Oan Oan
oa oa
Oa Oa
oap oap
Oap Oap
oarw oẳ
Oarw Oẳ
oasw oắ
Oasw Oắ
oat oat
Oat Oat
oawch oăch
Oawch Oăch
oawc oăc
Oawc Oăc
oawm oăm
Oawm Oăm
oawng oăng
Oawng Oăng
oawnh oănh
Oawnh Oănh
oawn oăn
Oawn Oăn
oaw oă
Oaw Oă
oawp oăp
Oawp Oăp
oawt oăt
Oawt Oăt
oaxw oẵ
Oaxw Oẵ
oay oay
Oay Oay
och och
Och Och
oc oc
Oc Oc
oe oe
Oe Oe
oich oich
Oich Oich
oic oic
Oic Oic
oim oim
Oim Oim
oing oing
Oing Oing
oinh oinh
Oinh Oinh
oin oin
Oin Oin
oi oi
Oi Oi
oip oip
Oip Oip
oit oit
Oit Oit
om om
Om Om
ong ong
Ong Ong
onh onh
Onh Onh
on on
On On
o o
O O
ooch ôch
Ooch Ôch
ooc ôc
Ooc Ôc
oom ôm
Oom Ôm
oong ông
Oong Ông
oonh ônh
Oonh Ônh
oon ôn
Oon Ôn
oo ô oo ô
Oo Ô
ooo oo
Ooo Oo
oop ôp
Oop Ôp
oot ôt
Oot Ôt
op op
Op Op
ot ot
Ot Ot
owch ơch
Owch Ơch
owc ơc
Owc Ơc
owm ơm
Owm Ơm
owng ơng
Owng Ơng
ownh ơnh
Ownh Ơnh
own ơn
Own Ơn
ow ơ ow ơ
Ow Ơ
owp ơp
Owp Ơp
owt ơt
Owt Ơt
oww ow
Oww Ow
uaach uâch
Uaach Uâch
uaac uâc
Uaac Uâc
uaam uâm
Uaam Uâm
uaang uâng
Uaang Uâng
uaanh uânh
Uaanh Uânh
uaan uân
Uaan Uân
uaap uâp
Uaap Uâp
uaat uât
Uaat Uât
uaa uâ
Uaa Uâ
uafa uầ
Uafa Uầ
uaja uậ
Uaja Uậ
uara uẩ
Uara Uẩ
uasa uấ
Uasa Uấ
ua ua
Ua Ua
uaxa uẫ
Uaxa Uẫ
uch uch
Uch Uch
uc uc
Uc Uc
uich uich
Uich Uich
uic uic
Uic Uic
uim uim
Uim Uim
uing uing
Uing Uing
uinh uinh
Uinh Uinh
uin uin
Uin Uin
uip uip
Uip Uip
uit uit
Uit Uit
ui ui
Ui Ui
um um
Um Um
ung ung
Ung Ung
unh unh
Unh Unh
un un
Un Un
uofo uồ
Uofo Uồ
uofw ườ
Uofw Ườ
uojo uộ
Uojo Uộ
uojw ượ
Uojw Ượ
uooch uôch
Uooch Uôch
uooc uôc
Uooc Uôc
uooi uôi
Uooi Uôi
uoom uôm
Uoom Uôm
uoong uông
Uoong Uông
uoonh uônh
Uoonh Uônh
uoon uôn
Uoon Uôn
uoop uôp
Uoop Uôp
uoot uôt
Uoot Uôt
uoo uô
Uoo Uô
uoro uổ
Uoro Uổ
uorw ưở
Uorw Ưở
uoso uố
Uoso Uố
uosw ướ
Uosw Ướ
uo uo
Uo Uo
uowch ươch
Uowch Ươch
uowc ươc
Uowc Ươc
uowi ươi
Uowi Ươi
uowm ươm
Uowm Ươm
uowng ương
Uowng Ương
uownh ươnh
Uownh Ươnh
uown ươn
Uown Ươn
uowp ươp
Uowp Ươp
uowt ươt
Uowt Ươt
uow ươ
Uow Ươ
uoxo uỗ
Uoxo Uỗ
uoxw ưỡ
Uoxw Ưỡ
up up
Up Up
ut ut
Ut Ut
u u
U U
uwa ưa
Uwa Ưa
uwch ưch
Uwch Ưch
uwc ưc
Uwc Ưc
uwm ưm
Uwm Ưm
uwng ưng
Uwng Ưng
uwnh ưnh
Uwnh Ưnh
uwn ưn
Uwn Ưn
uwofw ườ
Uwofw Ườ
uwojw ượ
Uwojw Ượ
uworw ưở
Uworw Ưở
uwosw ướ
Uwosw Ướ
uwow ươ
Uwow Ươ
uwoxw ưỡ
Uwoxw Ưỡ
uwp ưp
Uwp Ưp
uwt ưt
Uwt Ưt
uw ư uw ư
êe ee Uw Ư
âa aa uww uw
đd dd Uww Uw
ôo oo uya uya
Af À Uya Uya
af à uych uych
Ăf Ằ Uych Uych
ăf ằ uyc uyc
Âf Ầ Uyc Uyc
âf ầ uyee uyê
Ef È Uyee Uyê
ef è uym uym
Êf Ề Uym Uym
êf ề uyng uyng
If Ì Uyng Uyng
if ì uynh uynh
Of Ò Uynh Uynh
of ò uyn uyn
Ôf Ồ Uyn Uyn
ôf ồ uyp uyp
Ơf Ờ Uyp Uyp
ơf ờ uyt uyt
Uf Ù Uyt Uyt
uf ù uy uy
Ưf Ừ Uy Uy
ưf ừ ych ych
Yf Ỳ Ych Ych
yf ỳ yc yc
As Á Yc Yc
as á yeech yêch
Ăs Ắ Yeech Yêch
ăs ắ yeec yêc
Âs Ấ Yeec Yêc
âs ấ yeem yêm
Es É Yeem Yêm
es é yeeng yêng
Ês Ế Yeeng Yêng
ês ế yeenh yênh
Is Í Yeenh Yênh
is í yeen yên
Os Ó Yeen Yên
os ó yeep yêp
Ôs Ố Yeep Yêp
ôs ố yeet yêt
Ơs Ớ Yeet Yêt
ơs ớ yeeu yêu
Us Ú Yeeu Yêu
us ú yee yê
Ưs Ứ Yee Yê
ưs ứ yefe yề
Ys Ý Yefe Yề
ys ý yeje yệ
Ar Ả Yeje Yệ
ar ả yere yể
Ăr Ẳ Yere Yể
ăr ẳ yese yế
Âr Ẩ Yese Yế
âr ẩ yexe yễ
Er Ẻ Yexe Yễ
er ẻ ye ye
Êr Ể Ye Ye
êr ể ym ym
Ir Ỉ Ym Ym
ir ỉ yng yng
Or Ỏ Yng Yng
or ỏ ynh ynh
Ôr Ổ Ynh Ynh
ôr ổ yn yn
Ơr Ở Yn Yn
ơr ở yp yp
Ur Ủ Yp Yp
ur ủ yt yt
Ưr Ử Yt Yt
ưr ử y y
Yr Ỷ Y Y
yr ỷ \ \
Ax Ã
ax ã
Ăx Ẵ
ăx ẵ
Âx Ẫ
âx ẫ
Ex Ẽ
ex ẽ
Êx Ễ
êx ễ
Ix Ĩ
ix ĩ
Ox Õ
ox õ
Ôx Ỗ
ôx ỗ
Ơx Ỡ
ơx ỡ
Ux Ũ
ux ũ
Ưx Ữ
ưx ữ
Yx Ỹ
yx ỹ
Aj Ạ
aj ạ
Ăj Ặ
ăj ặ
Âj Ậ
âj ậ
Ej Ẹ
ej ẹ
Êj Ệ
êj ệ
Ij Ị
ij ị
Oj Ọ
oj ọ
Ôj Ộ
ôj ộ
Ơj Ợ
ơj ợ
Uj Ụ
uj ụ
Ưj Ự
ưj ự
Yj Ỵ
yj ỵ

2
run.sh
View File

@@ -4,6 +4,6 @@ pkill strans
pkill strans-xim pkill strans-xim
sleep 1 sleep 1
./strans map font/font.otf & ./strans map font &
sleep 1 sleep 1
xim/strans-xim & xim/strans-xim &

242
strans.c
View File

@@ -2,11 +2,21 @@
#include "fn.h" #include "fn.h"
static Im im; static Im im;
static void dictqmap(Im*);
static void
backmap(Im *im)
{
spopr(&im->pre);
}
Lang langs[] = { Lang langs[] = {
{LangJP, "hira", "kanji", nil, nil, jptrans}, {LangEN, "english", nil, transmap, backmap, dictqmap, nil, nil},
{LangJPK, "kata", "kanji", nil, nil, jptrans}, {LangJP, "hira", "kanji", transmap, backmap, dictqmap, nil, nil},
{LangKO, "hangul", "hangul", nil, nil, trans}, {LangJPK, "kata", "kanji", transmap, backmap, dictqmap, nil, nil},
{LangKO, "hangul", nil, transko, backko, dictqmap, nil, nil},
{LangEMOJI, "emoji", "emoji", transmap, backmap, dictqmap, nil, nil},
{LangVI, "telex", nil, transvi, backmap, dictqmap, nil, nil},
}; };
int nlang = nelem(langs); int nlang = nelem(langs);
@@ -14,7 +24,7 @@ static void
clearkouho(void) clearkouho(void)
{ {
im.nkouho = 0; im.nkouho = 0;
im.sel = 0; im.sel = -1;
} }
static void static void
@@ -33,41 +43,60 @@ static void
show(void) show(void)
{ {
Drawcmd dc; Drawcmd dc;
int i; int i, first, n;
sclear(&dc.preedit); sclear(&dc.pre);
mapget(im.l->map, &im.line, &dc.preedit); if(!mapget(im.l->map, &im.pre, &dc.pre))
dc.nkouho = im.nkouho; dc.pre = im.pre;
dc.sel = im.sel; first = im.sel >= Maxdisp ? im.sel - Maxdisp + 1 : 0;
for(i = 0; i < dc.nkouho; i++) n = im.nkouho - first;
dc.kouho[i] = im.kouho[i]; if(n > Maxdisp) n = Maxdisp;
dc.nkouho = n;
dc.sel = im.sel >= 0 ? im.sel - first : -1;
for(i = 0; i < n; i++)
dc.kouho[i] = im.kouho[first + i];
chansend(drawc, &dc); chansend(drawc, &dc);
} }
static void static void
dictq(void) reset(void)
{
sclear(&im.pre);
clearkouho();
show();
}
void
dictsend(Im *im, Str *key)
{ {
Str dict;
Dictreq req; Dictreq req;
if(!mapget(im.l->map, &im.line, &dict)){ req.key = *key;
req.lang = im->l->lang;
req.pre = im->pre;
channbsend(dictreqc, &req);
}
static void
dictqmap(Im *im)
{
Str dict;
if(!mapget(im->l->map, &im->pre, &dict)){
clearkouho(); clearkouho();
show(); show();
return; return;
} }
req.key = dict; dictsend(im, &dict);
req.lang = im.l->lang;
req.line = im.line;
channbsend(dictreqc, &req);
} }
static int static int
checklang(int c) setlang(int c)
{ {
Lang *l; Lang *l;
l = getlang(c); l = getlang(c);
if(l == nil && c != LangEN) if(l == nil)
return 0; return 0;
im.l = l; im.l = l;
return 1; return 1;
@@ -76,11 +105,13 @@ checklang(int c)
static void static void
commit(Str *com) commit(Str *com)
{ {
Str kana; Str val;
if(mapget(im.l->map, &im.line, &kana)) if(mapget(im.l->map, &im.pre, &val))
sappend(com, &kana); sappend(com, &val);
sclear(&im.line); else
sappend(com, &im.pre);
sclear(&im.pre);
} }
static int static int
@@ -90,18 +121,16 @@ dotrans(Rune c, Str *com)
Dictreq req; Dictreq req;
e = im.l->trans(&im, c); e = im.l->trans(&im, c);
if(e.s.n > 0) if(e.s.n > 0)
sappend(com, &e.s); sappend(com, &e.s);
sclear(&im.line); sclear(&im.pre);
sappend(&im.line, &e.next); sappend(&im.pre, &e.next);
if(e.eat && e.dict.n > 0){ if(e.eat && e.dict.n > 0){
req.key = e.dict; req.key = e.dict;
req.lang = im.l->lang; req.lang = im.l->lang;
req.line = im.line; req.pre = im.pre;
channbsend(dictreqc, &req); channbsend(dictreqc, &req);
} }
return e.eat; return e.eat;
} }
@@ -116,69 +145,52 @@ getlang(int lang)
return nil; return nil;
} }
static int
maplookup(Trie *t, Str *key, Str *out)
{
char buf[256], *v;
int klen, vlen;
if(key->n == 0)
return 0;
v = nil;
vlen = 0;
klen = stoutf(key, buf, sizeof(buf));
if(!trielookup(t, buf, klen, &v, &vlen))
return 0;
if(out != nil && v != nil)
sinit(out, v, vlen);
return 1;
}
Emit Emit
trans(Im *im, Rune c) transmap(Im *im, Rune c)
{ {
Emit e = {0}; Emit e = {0};
Str key, kana; Str key;
Hmap *h; Trie *t;
Rune last;
h = im->l->map; t = im->l->map;
key = im->line; key = im->pre;
sputr(&key, c); sputr(&key, c);
if(hmapget(h, &key)){ if(maplookup(t, &key, &e.dict)){
e.eat = 1; e.eat = 1;
e.next = key; e.next = key;
mapget(h, &key, &e.dict);
return e; return e;
} }
if(!mapget(t, &im->pre, &e.s))
last = slastr(&im->line); e.s = im->pre;
if(last == 0)
goto flush;
key = im->line;
key.n--;
if(mapget(h, &key, &kana)){
sclear(&key);
sputr(&key, last);
sputr(&key, c);
if(hmapget(h, &key)){
e.eat = 1;
e.s = kana;
sputr(&e.next, last);
sputr(&e.next, c);
mapget(h, &e.next, &e.dict);
return e;
}
}
flush:
mapget(h, &im->line, &e.s);
sclear(&key); sclear(&key);
sputr(&key, c); sputr(&key, c);
if(hmapget(h, &key) == nil){ if(!maplookup(t, &key, &e.dict)){
e.flush = 1;
sputr(&e.s, c); sputr(&e.s, c);
return e; return e;
} }
e.eat = 1; e.eat = 1;
sputr(&e.next, c); sputr(&e.next, c);
mapget(h, &e.next, &e.dict);
return e; return e;
} }
static void
reset(void)
{
sclear(&im.line);
clearkouho();
show();
}
static int static int
keystroke(u32int ks, u32int mod, Str *com) keystroke(u32int ks, u32int mod, Str *com)
{ {
@@ -191,7 +203,7 @@ keystroke(u32int ks, u32int mod, Str *com)
return 0; return 0;
if(ks == Kdown && im.sel < im.nkouho - 1) if(ks == Kdown && im.sel < im.nkouho - 1)
im.sel++; im.sel++;
if(ks == Kup && im.sel > 0) if(ks == Kup && im.sel >= 0)
im.sel--; im.sel--;
show(); show();
return 1; return 1;
@@ -211,7 +223,7 @@ keystroke(u32int ks, u32int mod, Str *com)
reset(); reset();
return 1; return 1;
} }
if(im.line.n > 0){ if(im.pre.n > 0){
commit(com); commit(com);
reset(); reset();
return 1; return 1;
@@ -219,18 +231,18 @@ keystroke(u32int ks, u32int mod, Str *com)
return 0; return 0;
} }
if(ks == Kback){ if(ks == Kback){
if(im.line.n == 0) if(im.pre.n == 0)
return 0; return 0;
spopr(&im.line); im.l->back(&im);
if(im.line.n == 0){ if(im.pre.n == 0){
reset(); reset();
return 1; return 1;
} }
dictq(); im.l->dictq(&im);
return 1; return 1;
} }
if(ks == Kesc){ if(ks == Kesc){
if(im.line.n == 0) if(im.pre.n == 0)
return 0; return 0;
reset(); reset();
return 1; return 1;
@@ -244,25 +256,31 @@ keystroke(u32int ks, u32int mod, Str *com)
reset(); reset();
if(ks >= 'a' && ks <= 'z') if(ks >= 'a' && ks <= 'z')
ks -= 'a' - 1; ks -= 'a' - 1;
if(checklang(ks)) if(setlang(ks))
return 1; return 1;
return 0; return 0;
} }
if(im.l == nil) if(ks == '0' && im.nkouho > 0){
return 0; commit(com);
if(ks > 0x7f || !isalpha(ks)){ reset();
return 1;
}
if(ks > 0x7f || ks == ' '){
commit(com); commit(com);
sputr(com, ks); sputr(com, ks);
reset(); reset();
return 1; return 1;
} }
return dotrans(ks, com); dotrans(ks, com);
show();
return 1;
} }
static void static void
init(void) init(void)
{ {
memset(&im, 0, sizeof(im)); memset(&im, 0, sizeof(im));
im.l = getlang(LangEN);
} }
void void
@@ -297,7 +315,7 @@ imthread(void*)
write(kr.fd, out, n); write(kr.fd, out, n);
break; break;
case 1: case 1:
if(scmp(&res.key, &im.line) == 0){ if(scmp(&res.key, &im.pre) == 0){
setkouho(&res); setkouho(&res);
show(); show();
} }
@@ -307,57 +325,21 @@ imthread(void*)
} }
int int
mapget(Hmap *h, Str *key, Str *out) mapget(Trie *t, Str *key, Str *out)
{ {
Hnode *n; char buf[256], *v;
int klen, vlen;
if(key->n == 0) if(key->n == 0)
return 0; return 0;
n = hmapget(h, key); klen = stoutf(key, buf, sizeof(buf));
if(n == nil || n->kanalen == 0) v = trieget(t, buf, klen, &vlen);
if(v == nil)
return 0; return 0;
sinit(out, n->kana, n->kanalen); sinit(out, v, vlen);
return 1; return 1;
} }
static Hmap*
openmap(char *path)
{
Hmap *h;
Biobuf *b;
Str key, val, empty;
char *line, *tab;
int i, klen;
b = Bopen(path, OREAD);
if(b == nil)
die("can't open: %s", path);
h = hmapalloc(1024, 0);
sclear(&empty);
while((line = Brdstr(b, '\n', 1)) != nil){
if(line[0] == '\0'){
free(line);
continue;
}
tab = strchr(line, '\t');
if(tab == nil || tab[1] == '\0')
die("malformed map: %s", path);
*tab = '\0';
klen = strlen(line);
sinit(&key, line, klen);
sinit(&val, tab+1, strlen(tab+1));
hmapset(&h, &key, &val);
for(i = 1; i < klen; i++){
sinit(&key, line, i);
if(hmapget(h, &key) == nil)
hmapset(&h, &key, &empty);
}
free(line);
}
Bterm(b);
return h;
}
void void
mapinit(char *dir) mapinit(char *dir)
{ {
@@ -368,7 +350,7 @@ mapinit(char *dir)
if(langs[i].mapname == nil) if(langs[i].mapname == nil)
continue; continue;
snprint(path, sizeof(path), "%s/%s.map", dir, langs[i].mapname); snprint(path, sizeof(path), "%s/%s.map", dir, langs[i].mapname);
langs[i].map = openmap(path); langs[i].map = trieopen(path);
} }
} }

130
trie.c Normal file
View File

@@ -0,0 +1,130 @@
#include "dat.h"
#include "fn.h"
static int
newnode(Trie *t)
{
int i;
if(t->n >= t->cap){
t->cap *= 2;
t->nodes = erealloc(t->nodes, t->cap * sizeof(Tnode));
}
i = t->n++;
memset(&t->nodes[i], 0, sizeof(Tnode));
t->nodes[i].child = -1;
t->nodes[i].sibling = -1;
return i;
}
static int
find(Trie *t, int ni, char c)
{
int pi;
for(pi = t->nodes[ni].child; pi >= 0; pi = t->nodes[pi].sibling)
if(t->nodes[pi].c == c)
return pi;
return -1;
}
static int
add(Trie *t, int ni, char c)
{
int pi;
pi = newnode(t);
t->nodes[pi].c = c;
t->nodes[pi].sibling = t->nodes[ni].child;
t->nodes[ni].child = pi;
return pi;
}
static void
insert(Trie *t, char *key, int klen, char *val, int vlen)
{
int ni, ci;
int i;
ni = t->root;
for(i = 0; i < klen; i++){
ci = find(t, ni, key[i]);
if(ci < 0)
ci = add(t, ni, key[i]);
ni = ci;
}
t->nodes[ni].val = val;
t->nodes[ni].vlen = vlen;
}
Trie*
trieopen(char *path)
{
Trie *t;
Biobuf *b;
char *line, *tab, *key, *val;
int klen, vlen;
b = Bopen(path, OREAD);
if(b == nil)
die("can't open: %s", path);
t = emalloc(sizeof(*t));
t->cap = 1024;
t->nodes = emalloc(t->cap * sizeof(Tnode));
t->n = 0;
t->root = newnode(t);
while((line = Brdstr(b, '\n', 1)) != nil){
if(line[0] == '\0'){
free(line);
continue;
}
tab = strchr(line, '\t');
if(tab == nil || tab[1] == '\0')
die("malformed map: %s", path);
*tab = '\0';
key = line;
klen = tab - line;
val = tab + 1;
vlen = strlen(val);
insert(t, key, klen, val, vlen);
}
Bterm(b);
return t;
}
char*
trieget(Trie *t, char *key, int klen, int *vlen)
{
int ni;
int i;
ni = t->root;
for(i = 0; i < klen; i++){
ni = find(t, ni, key[i]);
if(ni < 0)
return nil;
}
if(t->nodes[ni].val == nil)
return nil;
*vlen = t->nodes[ni].vlen;
return t->nodes[ni].val;
}
int
trielookup(Trie *t, char *key, int klen, char **val, int *vlen)
{
int ni;
int i;
ni = t->root;
for(i = 0; i < klen; i++){
ni = find(t, ni, key[i]);
if(ni < 0)
return 0;
}
if(val != nil && t->nodes[ni].val != nil){
*val = t->nodes[ni].val;
*vlen = t->nodes[ni].vlen;
}
return 1;
}

148
vi.c Normal file
View File

@@ -0,0 +1,148 @@
#include "dat.h"
#include "fn.h"
static struct {
Rune base;
Rune tone[5]; /* s f r x j */
} vitone[] = {
{L'a', {L'á', L'à', L'', L'ã', L''}},
{L'â', {L'', L'', L'', L'', L''}},
{L'ă', {L'', L'', L'', L'', L''}},
{L'e', {L'é', L'è', L'', L'', L''}},
{L'ê', {L'ế', L'', L'', L'', L''}},
{L'i', {L'í', L'ì', L'', L'ĩ', L''}},
{L'o', {L'ó', L'ò', L'', L'õ', L''}},
{L'ô', {L'', L'', L'', L'', L''}},
{L'ơ', {L'', L'', L'', L'', L''}},
{L'u', {L'ú', L'ù', L'', L'ũ', L''}},
{L'ư', {L'', L'', L'', L'', L''}},
{L'y', {L'ý', L'', L'', L'', L''}},
{L'A', {L'Á', L'À', L'', L'Ã', L''}},
{L'Â', {L'', L'', L'', L'', L''}},
{L'Ă', {L'', L'', L'', L'', L''}},
{L'E', {L'É', L'È', L'', L'', L''}},
{L'Ê', {L'', L'', L'', L'', L''}},
{L'I', {L'Í', L'Ì', L'', L'Ĩ', L''}},
{L'O', {L'Ó', L'Ò', L'', L'Õ', L''}},
{L'Ô', {L'', L'', L'', L'', L''}},
{L'Ơ', {L'', L'', L'', L'', L''}},
{L'U', {L'Ú', L'Ù', L'', L'Ũ', L''}},
{L'Ư', {L'', L'', L'', L'', L''}},
{L'Y', {L'Ý', L'', L'', L'', L''}},
};
static int tonetab[128] = {
['s'] = 1, ['f'] = 2, ['r'] = 3, ['x'] = 4, ['j'] = 5,
};
#define Istone(c) ((c) < 128 && tonetab[(c)] > 0)
#define Toneidx(c) (tonetab[(c)] - 1)
static int
isvowel(Rune c)
{
int i;
for(i = 0; i < nelem(vitone); i++)
if(vitone[i].base == c)
return 1;
return 0;
}
static Rune
removetone(Rune c)
{
int i, j;
for(i = 0; i < nelem(vitone); i++){
if(vitone[i].base == c)
return c;
for(j = 0; j < 5; j++)
if(vitone[i].tone[j] == c)
return vitone[i].base;
}
return c;
}
static Rune
applytone(Rune c, int tidx)
{
int i;
Rune base;
base = removetone(c);
for(i = 0; i < nelem(vitone); i++)
if(vitone[i].base == base)
return vitone[i].tone[tidx];
return c;
}
Emit
transvi(Im *im, Rune c)
{
Emit e;
Str mapped, pre;
int i, tidx, vi, last, penult;
Rune v, b1, b2;
if(!Istone(c) && c != 'z')
return transmap(im, c);
memset(&e, 0, sizeof e);
if(im->pre.n == 0){
sputr(&e.s, c);
return e;
}
if(im->pre.r[im->pre.n - 1] == '\\'){
pre = im->pre;
pre.n--;
if(!mapget(im->l->map, &pre, &mapped))
mapped = pre;
sputr(&mapped, c);
e.eat = 1;
e.s = mapped;
return e;
}
if(!mapget(im->l->map, &im->pre, &mapped))
mapped = im->pre;
last = -1;
penult = -1;
for(i = 0; i < mapped.n; i++){
v = removetone(mapped.r[i]);
if(isvowel(v)){
penult = last;
last = i;
}
}
vi = -1;
if(last >= 0){
if(last < mapped.n - 1 || penult < 0)
vi = last;
else{
b1 = removetone(mapped.r[penult]);
b2 = removetone(mapped.r[last]);
if((b1 == 'o' || b1 == 'O') && (b2 == 'a' || b2 == 'A' || b2 == 'e' || b2 == 'E'))
vi = last;
else if((b1 == 'u' || b1 == 'U') && (b2 == 'y' || b2 == 'Y'))
vi = last;
else
vi = penult;
}
}
if(vi < 0){
e.eat = 1;
e.s = mapped;
sputr(&e.s, c);
return e;
}
if(c == 'z')
mapped.r[vi] = removetone(mapped.r[vi]);
else{
tidx = Toneidx(c);
mapped.r[vi] = applytone(mapped.r[vi], tidx);
}
e.eat = 1;
e.next = mapped;
return e;
}

27
win.c
View File

@@ -6,7 +6,7 @@ enum {
Asciitofull = 0xFEE0, Asciitofull = 0xFEE0,
}; };
extern char *fontpath; extern char *fontdir;
static xcb_connection_t *conn; static xcb_connection_t *conn;
static xcb_screen_t *scr; static xcb_screen_t *scr;
@@ -52,7 +52,7 @@ wininit(void)
gc = xcb_generate_id(conn); gc = xcb_generate_id(conn);
xcb_create_gc(conn, gc, win, 0, nil); xcb_create_gc(conn, gc, win, 0, nil);
img = emalloc(Imgw * Imgh * sizeof(img[0])); img = emalloc(Imgw * Imgh * sizeof(img[0]));
fontinit(fontpath); fontinit(fontdir);
} }
static void static void
@@ -71,9 +71,11 @@ drawkouho(Drawcmd *dc, int first, int n, int w, int h)
Str *s; Str *s;
memset(img, (uchar)Colbg, w * h * sizeof(u32int)); memset(img, (uchar)Colbg, w * h * sizeof(u32int));
drawstr(img, 0, 0, dc->preedit.r, dc->preedit.n, w, h); drawstr(img, 0, 0, dc->pre.r, dc->pre.n, w, h);
sely = Fontsz + (dc->sel - first) * Fontsz; if(dc->sel >= 0){
memset(img + sely * w, (uchar)Colsel, Fontsz * w * sizeof(u32int)); sely = Fontsz + dc->sel * Fontsz;
memset(img + sely * w, (uchar)Colsel, Fontsz * w * sizeof(u32int));
}
for(i = 0, y = Fontsz; i < n; i++, y += Fontsz){ for(i = 0, y = Fontsz; i < n; i++, y += Fontsz){
s = &dc->kouho[first+i]; s = &dc->kouho[first+i];
putfont(img, w, h, 0, y, '1' + i + Asciitofull); putfont(img, w, h, 0, y, '1' + i + Asciitofull);
@@ -99,17 +101,16 @@ winhide(void)
static void static void
winshow(Drawcmd *dc) winshow(Drawcmd *dc)
{ {
int px, py, w, h, i, n, first, maxw; int px, py, w, h, i, n, maxw;
u32int vals[4]; u32int vals[4];
xcb_query_pointer_reply_t *ptr; xcb_query_pointer_reply_t *ptr;
xcb_query_pointer_cookie_t cookie; xcb_query_pointer_cookie_t cookie;
cookie = xcb_query_pointer(conn, scr->root); cookie = xcb_query_pointer(conn, scr->root);
first = dc->sel >= Maxdisp ? dc->sel - Maxdisp + 1 : 0; n = dc->nkouho;
n = min(dc->nkouho - first, Maxdisp); maxw = dc->pre.n;
maxw = dc->preedit.n;
for(i = 0; i < n; i++) for(i = 0; i < n; i++)
maxw = max(maxw, dc->kouho[first+i].n); maxw = max(maxw, dc->kouho[i].n);
ptr = xcb_query_pointer_reply(conn, cookie, nil); ptr = xcb_query_pointer_reply(conn, cookie, nil);
if(ptr == nil) if(ptr == nil)
die("xcb_query_pointer"); die("xcb_query_pointer");
@@ -125,7 +126,7 @@ winshow(Drawcmd *dc)
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
vals); vals);
xcb_map_window(conn, win); xcb_map_window(conn, win);
drawkouho(dc, first, n, w, h); drawkouho(dc, 0, n, w, h);
putimage(w, h); putimage(w, h);
} }
@@ -137,7 +138,9 @@ drawthread(void*)
threadsetname("draw"); threadsetname("draw");
wininit(); wininit();
while(chanrecv(drawc, &dc) > 0){ while(chanrecv(drawc, &dc) > 0){
if(dc.nkouho == 0 && dc.preedit.n == 0) while(channbrecv(drawc, &dc) > 0)
;
if(dc.nkouho == 0 && dc.pre.n == 0)
winhide(); winhide();
else else
winshow(&dc); winshow(&dc);