1 ;;; egg/sj3.el --- SJ3 Support (high level interface) in Egg
2 ;;; Input Method Architecture
4 ;; Copyright (C) 1999, 2000 Free Software Foundation, Inc
5 ;; 2015 Hiroki Sato <hrs@allbsd.org>
7 ;; Author: NIIBE Yutaka <gniibe@chroot.org>
9 ;; Keywords: mule, multilingual, input method
11 ;; This file is part of EGG.
13 ;; EGG is free software; you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 2, or (at your option)
18 ;; EGG is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details.
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs; see the file COPYING. If not, write to the
25 ;; Free Software Foundation, Inc.,
26 ;; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
37 "SJ3 interface for Tamago-tsunagi."
40 (defcustom sj3-hostname "localhost"
41 "Hostname of SJ3 server"
42 :group 'sj3 :type 'string)
44 (defcustom sj3-server-port 3086
45 "Port number of SJ3 server"
46 :group 'sj3 :type 'integer)
50 (defmacro SJ3-const (c)
51 (cond ((eq c 'FileNotExist) 35)
56 (sj3-register-1 "
\e$BEPO?<-=qL>
\e(B:")
57 (sj3-register-2 "
\e$BIJ;lL>
\e(B"))))
59 (defvar sj3-hinshi-menu
61 (menu "
\e$BIJ;l
\e(B:
\e$BL>;l
\e(B:"
63 ("
\e$BL>;l
\e(B(
\e$B$*!D
\e(B)" . 2)
64 ("
\e$BL>;l
\e(B(
\e$B$4!D
\e(B)" . 3)
65 ("
\e$BL>;l
\e(B(
\e$B!DE*
\e(B/
\e$B2=
\e(B)" . 4)
66 ("
\e$BL>;l
\e(B(
\e$B$*!D$9$k
\e(B)" . 5)
67 ("
\e$BL>;l
\e(B(
\e$B!D$9$k
\e(B)" . 6)
68 ("
\e$BL>;l
\e(B(
\e$B$4!D$9$k
\e(B)" . 7)
69 ("
\e$BL>;l
\e(B(
\e$B!D$J
\e(B/
\e$B$K
\e(B)" . 8)
70 ("
\e$BL>;l
\e(B(
\e$B$*!D$J
\e(B/
\e$B$K
\e(B)" . 9)
71 ("
\e$BL>;l
\e(B(
\e$B$4!D$J
\e(B/
\e$B$K
\e(B)" . 10)
72 ("
\e$BL>;l
\e(B(
\e$BI{;l
\e(B)" . 11))))
73 ("
\e$BBeL>;l
\e(B" . 12)
77 ("
\e$B8)
\e(B/
\e$B6hL>
\e(B" . 25)
79 (menu "
\e$BIJ;l
\e(B:
\e$BF0;l
\e(B:"
80 (("
\e$B%5JQ8l44
\e(B" . 80)
81 ("
\e$B%6JQ8l44
\e(B" . 81)
82 ("
\e$B0lCJITJQ2=It
\e(B" . 90)
83 ("
\e$B%+9T8^CJ8l44
\e(B" . 91)
84 ("
\e$B%,9T8^CJ8l44
\e(B" . 92)
85 ("
\e$B%59T8^CJ8l44
\e(B" . 93)
86 ("
\e$B%?9T8^CJ8l44
\e(B" . 94)
87 ("
\e$B%J9T8^CJ8l44
\e(B" . 95)
88 ("
\e$B%P9T8^CJ8l44
\e(B" . 96)
89 ("
\e$B%^9T8^CJ8l44
\e(B" . 97)
90 ("
\e$B%i9T8^CJ8l44
\e(B" . 98)
91 ("
\e$B%o9T8^CJ8l44
\e(B" . 99))))
92 ("
\e$BO"BN;l
\e(B" . 26)
93 ("
\e$B@\B3;l
\e(B" . 27)
94 ("
\e$B=u?t;l
\e(B" . 29)
96 ("
\e$B@\F,8l
\e(B" . 31)
97 ("
\e$B@\Hx8l
\e(B" . 36)
99 ("
\e$BI{;l
\e(B2" . 46)
100 ("
\e$B7AMF;l8l44
\e(B" . 60)
101 ("
\e$B7AMFF0;l8l44
\e(B" . 71)
102 ("
\e$BC14A;z
\e(B" . 189))
103 "Menu data for a hinshi (a part of speech) selection.")
105 (defun sj3-hinshi-name (id &optional menu alist)
106 "Return a hinshi (a part of speech) name corresponding to ID.
107 If ID is nil, return a flattened alist from `sj3-hinshi-menu'.
108 Don't specify the optional arguments in normal use."
109 (let ((menu (or menu sj3-hinshi-menu)))
111 (if (consp (cdr menu))
112 (mapcar (lambda (elem)
113 (setq alist (sj3-hinshi-name nil elem alist)))
115 (setq alist (nconc alist (list (cons (cdr menu) (car menu)))))))
117 (cdr (assq id alist))
120 (setplist 'sj3-conversion-backend
121 '(egg-start-conversion sj3-start-conversion
122 egg-get-bunsetsu-source sj3-get-bunsetsu-source
123 egg-get-bunsetsu-converted sj3-get-bunsetsu-converted
124 egg-get-source-language sj3-get-source-language
125 egg-get-converted-language sj3-get-converted-language
126 egg-list-candidates sj3-list-candidates
127 egg-decide-candidate sj3-decide-candidate
128 egg-change-bunsetsu-length sj3-change-bunsetsu-length
129 egg-end-conversion sj3-end-conversion
130 egg-word-registration sj3-word-registration))
132 (defconst sj3-backend-alist '((Japanese ((sj3-conversion-backend)))))
134 (egg-set-finalize-backend '(sj3-finalize-backend))
136 (defvar sj3-stdy-size 0 "STDYSIZE of SJ3 server")
138 (defvar sj3-open-message)
140 (defun sj3-open (hostname)
141 "Establish the connection to SJ3 server. Return process object."
142 (let* ((buf (generate-new-buffer " *SJ3*"))
145 (setq proc (open-network-stream "SJ3" buf hostname sj3-server-port))
147 (egg-error "failed to connect sj3 server")))
148 (set-process-query-on-exit-flag proc nil)
149 (set-process-coding-system proc 'binary 'binary)
150 (set-marker-insertion-type (process-mark proc) t)
151 (with-current-buffer buf
153 (buffer-disable-undo)
154 (set-buffer-multibyte nil))
155 ;; Initialize dictionaries
156 (setq sj3-sys-dict-list nil)
157 (setq sj3-user-dict-list nil)
158 (setq result (sj3rpc-open proc (system-name) (user-login-name)))
160 (let ((msg (sj3rpc-get-error-message (- result))))
161 (delete-process proc)
163 (egg-error "Can't open SJ3 session (%s): %s" hostname msg)))
164 (setq result (sj3rpc-get-stdy-size proc))
166 (let ((msg (sj3rpc-get-error-message (- result))))
167 (delete-process proc)
169 (egg-error "Can't get SJ3 STDYSIZE: %s"msg)))
170 (setq sj3-stdy-size result)
173 ;; (defun sj3-open (hostname-list)
174 ;; "Establish the connection to SJ3 server. Return process object."
175 ;; (let* ((buf (generate-new-buffer " *SJ3*"))
176 ;; (msg-form "SJ3: connecting to sj3serv at %s...")
177 ;; hostname proc result msg)
178 ;; (with-current-buffer buf
180 ;; (buffer-disable-undo)
181 ;; (setq enable-multibyte-characters nil))
183 ;; ((null hostname-list)
184 ;; (setq hostname-list '("localhost")))
185 ;; ((null (listp hostname-list))
186 ;; (setq hostname-list (list hostname-list))))
187 ;; (while (and hostname-list (null proc))
188 ;; (setq hostname (car hostname-list)
189 ;; hostname-list (cdr hostname-list))
190 ;; (message msg-form hostname)
192 ;; (condition-case result
193 ;; (setq proc (open-network-stream "SJ3" buf hostname sj3-server-port))
197 ;; (process-kill-without-query proc)
198 ;; (set-process-coding-system proc 'binary 'binary)
199 ;; (set-marker-insertion-type (process-mark proc) t)
200 ;; ;; Initialize dictionaries
201 ;; (setq sj3-sys-dict-list nil)
202 ;; (setq sj3-user-dict-list nil)
203 ;; (setq result (sj3rpc-open proc (system-name) (user-login-name)))
206 ;; (delete-process proc)
208 ;; msg (format "Can't open SJ3 session (%s): %s"
210 ;; (setq result (sj3rpc-get-stdy-size proc))
213 ;; (delete-process proc)
215 ;; msg (format "Can't get SJ3 STDYSIZE: %s"
216 ;; (sj3rpc-get-error-message (- result)))))
217 ;; (setq sj3-stdy-size result))))))
220 ;; (setq sj3-open-message (format (concat msg-form "done") hostname))
223 ;; (error "%s" (or msg "no sj3serv available")))))
225 ;; <env> ::= [ <proc> <dictionary-list> ]
226 (defvar sj3-environment nil
227 "Environment for SJ3 kana-kanji conversion")
229 (defsubst sj3env-get-proc (env)
231 (defsubst sj3env-get-dictionary-list (env)
234 ;; <sj3-bunsetsu> ::=
235 ;; [ <env> <source> <converted> <rest> <stdy>
236 ;; <zenkouho> <zenkouho-pos> <zenkouho-converted>
237 ;; <kugiri-changed> ]
238 (defsubst sj3-make-bunsetsu (env source converted rest stdy)
240 'sj3-conversion-backend
241 (vector env source converted rest stdy nil nil nil nil nil)))
243 (defsubst sj3bunsetsu-get-env (b)
244 (aref (egg-bunsetsu-get-info b) 0))
245 (defsubst sj3bunsetsu-get-source (b)
246 (aref (egg-bunsetsu-get-info b) 1))
247 (defsubst sj3bunsetsu-get-converted (b)
248 (aref (egg-bunsetsu-get-info b) 2))
249 (defsubst sj3bunsetsu-get-rest (b)
250 (aref (egg-bunsetsu-get-info b) 3))
251 (defsubst sj3bunsetsu-get-stdy (b)
252 (aref (egg-bunsetsu-get-info b) 4))
254 (defsubst sj3bunsetsu-get-zenkouho (b)
255 (aref (egg-bunsetsu-get-info b) 5))
256 (defsubst sj3bunsetsu-set-zenkouho (b z)
257 (aset (egg-bunsetsu-get-info b) 5 z))
259 (defsubst sj3bunsetsu-get-zenkouho-pos (b)
260 (aref (egg-bunsetsu-get-info b) 6))
261 (defsubst sj3bunsetsu-set-zenkouho-pos (b zp)
262 (aset (egg-bunsetsu-get-info b) 6 zp))
264 (defsubst sj3bunsetsu-get-zenkouho-converted (b)
265 (aref (egg-bunsetsu-get-info b) 7))
266 (defsubst sj3bunsetsu-set-zenkouho-converted (b zc)
267 (aset (egg-bunsetsu-get-info b) 7 zc))
269 (defsubst sj3bunsetsu-get-kugiri-changed (b)
270 (aref (egg-bunsetsu-get-info b) 8))
271 (defsubst sj3bunsetsu-set-kugiri-changed (b s)
272 (aset (egg-bunsetsu-get-info b) 8 s))
274 (defun sj3-get-bunsetsu-source (b)
275 (sj3bunsetsu-get-source b))
277 (defun sj3-get-bunsetsu-converted (b)
278 (concat (sj3bunsetsu-get-converted b) (sj3bunsetsu-get-rest b)))
280 (defun sj3-get-source-language (b) 'Japanese)
281 (defun sj3-get-converted-language (b) 'Japanese)
282 (defun sj3-get-bunsetsu-stdy (b) (sj3bunsetsu-get-stdy b))
284 (defvar sj3-dictionary-specification
287 [("private.dic") ""])
288 "Dictionary specification of SJ3.")
290 (defvar sj3-usr-dic-dir (concat "user/" (user-login-name))
291 "*Directory of user dictionary for SJ3.")
293 (defun sj3-filename (p)
295 (cond ((consp p) (concat sj3-usr-dic-dir "/" (car p)))
298 (defun sj3-get-environment ()
299 "Return the backend of SJ3 environment."
302 (let* ((proc (sj3-open sj3-hostname))
303 (freq-info-name (sj3-filename (car sj3-dictionary-specification)))
304 (l (cdr sj3-dictionary-specification))
306 (sj3-open-freq-info proc freq-info-name)
311 (sj3-open-dictionary proc (sj3-filename (aref dic 0))
314 (egg-error "Dame2") ; XXX
315 (setq dict-list (cons dic-id dict-list)
317 (setq sj3-environment (vector proc dict-list)))))
319 (defun sj3-open-freq-info (proc name)
323 (setq ret (sj3rpc-open-stdy proc name))
326 (message "
\e$B3X=,%U%!%$%k
\e(B(%s)
\e$B$,$"$j$^$;$s
\e(B" name)
327 (if (/= ret (SJ3-const FileNotExist))
328 (egg-error "Fatal1") ; XXX
330 (format "
\e$B3X=,%U%!%$%k
\e(B(%s)
\e$B$,$"$j$^$;$s!#:n$j$^$9$+
\e(B? "
332 (sj3rpc-make-directory proc
333 (file-name-directory name))
335 (= (sj3rpc-make-stdy proc name) 0))
336 (message "
\e$B3X=,%U%!%$%k
\e(B(%s)
\e$B$r:n$j$^$7$?
\e(B" name)
337 (egg-error "Fatal2"))))))) ; XXX
339 (defun sj3-open-dictionary (proc name passwd)
343 (setq ret (sj3rpc-open-dictionary proc name passwd))
346 (message "
\e$B<-=q%U%!%$%k
\e(B(%s)
\e$B$,$"$j$^$;$s
\e(B" name)
347 (setq ret (- ret)) ; Get error code.
348 (if (/= ret (SJ3-const FileNotExist))
349 (egg-error "Fatal3 %d" ret) ; XXX
351 (format "
\e$B<-=q%U%!%$%k
\e(B(%s)
\e$B$,$"$j$^$;$s!#:n$j$^$9$+
\e(B? "
353 (= (sj3rpc-make-dictionary proc name) 0))
354 (message "
\e$B<-=q%U%!%$%k
\e(B(%s)
\e$B$r:n$j$^$7$?
\e(B" name)
355 (egg-error "Fatal4"))))) ; XXX
358 (defun sj3-start-conversion (backend yomi &optional context)
359 "Convert YOMI string to kanji, and enter conversion mode.
360 Return the list of bunsetsu."
361 (let ((env (sj3-get-environment)))
362 (sj3rpc-begin env yomi)))
364 (defun sj3-end-conversion (bunsetsu-list abort)
367 (let ((env (sj3bunsetsu-get-env (car bunsetsu-list)))
369 bunsetsu stdy kugiri-changed)
371 (setq bunsetsu (car l))
373 (setq stdy (sj3bunsetsu-get-stdy bunsetsu))
375 (sj3rpc-bunsetsu-stdy env stdy))
377 (setq kugiri-changed (sj3bunsetsu-get-kugiri-changed
379 (let ((yomi1 (sj3bunsetsu-get-source bunsetsu))
380 (yomi2 (sj3bunsetsu-get-source (car l))))
381 (if (/= kugiri-changed (length yomi1))
382 (sj3rpc-kugiri-stdy env yomi1 yomi2
383 (sj3bunsetsu-get-stdy (car l))))))))))
385 (defun sj3-list-candidates (bunsetsu prev-bunsetsu next-bunsetsu major)
386 (setq bunsetsu (car bunsetsu))
387 (if (sj3bunsetsu-get-zenkouho bunsetsu)
388 (cons (sj3bunsetsu-get-zenkouho-pos bunsetsu)
389 (sj3bunsetsu-get-zenkouho-converted bunsetsu))
390 (let* ((env (sj3bunsetsu-get-env bunsetsu))
391 (yomi (sj3bunsetsu-get-source bunsetsu))
392 (z (sj3rpc-get-bunsetsu-candidates env yomi)))
393 (sj3bunsetsu-set-zenkouho bunsetsu z)
394 (cons (sj3bunsetsu-set-zenkouho-pos bunsetsu 0)
395 (sj3bunsetsu-set-zenkouho-converted
397 (mapcar 'sj3bunsetsu-get-converted z))))))
399 (defun sj3-decide-candidate (bunsetsu candidate-pos prev-b next-b)
400 (setq bunsetsu (car bunsetsu))
401 (let* ((candidate-list (sj3bunsetsu-get-zenkouho bunsetsu))
402 (candidate (nth candidate-pos candidate-list)))
403 (sj3bunsetsu-set-zenkouho candidate candidate-list)
404 (sj3bunsetsu-set-zenkouho-pos candidate candidate-pos)
405 (sj3bunsetsu-set-zenkouho-converted
406 candidate (sj3bunsetsu-get-zenkouho-converted bunsetsu))
407 (list (list candidate))))
409 (defun sj3-change-bunsetsu-length (bunsetsu prev-b next-b len major)
410 (let ((yomi (mapconcat 'sj3bunsetsu-get-source bunsetsu nil))
411 (env (sj3bunsetsu-get-env (car bunsetsu)))
414 (setq yomi1 (substring yomi 0 len)
415 yomi2 (substring yomi len))
416 (setq new (sj3rpc-tanbunsetsu-conversion env yomi1))
417 ;; Only set once (memory original length of the bunsetsu).
418 (sj3bunsetsu-set-kugiri-changed new
419 (or (sj3bunsetsu-get-kugiri-changed old)
420 (length (sj3bunsetsu-get-source old))))
421 (if (> (length yomi2) 0)
422 (list (list new (sj3rpc-tanbunsetsu-conversion env yomi2)))
425 (defun sj3-finalize-backend ()
427 (let ((proc (sj3env-get-proc sj3-environment))
428 (dict-list (sj3env-get-dictionary-list sj3-environment))
431 (setq dict (car dict-list))
432 (setq dict-list (cdr dict-list))
433 (sj3rpc-close-dictionary proc dict)) ; XXX: check error
434 (sj3rpc-close-stdy proc)
436 (setq sj3-environment nil))))
438 ;;; word registration
440 (defun sj3-dictionary-select ()
441 (menudiag-select (list 'menu
442 (egg-get-message 'sj3-register-1)
443 (aref (nth 2 sj3-dictionary-specification) 0))))
445 (defun sj3-hinshi-select ()
446 (menudiag-select (list 'menu
447 (egg-get-message 'sj3-register-2)
450 (defun sj3-word-registration (backend kanji yomi)
451 "Register a word KANJI with a pronunciation YOMI."
452 (if (or (null (eq (egg-get-language 0 kanji)
453 (sj3-get-converted-language backend)))
454 (next-single-property-change 0 'egg-lang kanji)
455 (null (eq (egg-get-language 0 yomi)
456 (sj3-get-source-language backend)))
457 (next-single-property-change 0 'egg-lang yomi))
458 (egg-error "word registration: invalid character")
459 (let* ((env (sj3-get-environment))
460 (dic (sj3-dictionary-select))
461 (hinshi-id (sj3-hinshi-select))
462 (result (sj3rpc-add-word env
464 yomi kanji hinshi-id)))
466 (list (sj3-hinshi-name hinshi-id) dic)
467 (egg-error (sj3rpc-get-error-message (- result)))))))
472 (run-hooks 'sj3-load-hook)
475 (defun egg-activate-sj3 (&rest arg)
476 "Activate SJ3 backend of Tamago-tsunagi."
477 (apply 'egg-mode (append arg sj3-backend-alist)))
479 ;;; egg/sj3.el ends here.