d1d1f56100938db241eb13a87597ded959f3fe07
[elisp/tamago.git] / egg / sj3.el
1 ;;; egg/sj3.el --- SJ3 Support (high level interface) in Egg
2 ;;;                Input Method Architecture
3
4 ;; Copyright (C) 1999, 2000 Free Software Foundation, Inc
5
6 ;; Author: NIIBE Yutaka <gniibe@chroot.org>
7
8 ;; Maintainer: TOMURA Satoru <tomura@etl.go.jp>
9
10 ;; Keywords: mule, multilingual, input method
11
12 ;; This file is part of EGG.
13
14 ;; EGG is free software; you can redistribute it and/or modify
15 ;; it under the terms of the GNU General Public License as published by
16 ;; the Free Software Foundation; either version 2, or (at your option)
17 ;; any later version.
18
19 ;; EGG is distributed in the hope that it will be useful,
20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 ;; GNU General Public License for more details.
23
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
26 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
27 ;; Boston, MA 02111-1307, USA.
28
29 ;;; Commentary:
30
31
32 ;;; Code:
33
34
35 (require 'egg-edep)
36
37 (eval-when-compile
38   (defmacro SJ3-const (c)
39     (cond ((eq c 'FileNotExist) 35)
40           )))
41
42 (setplist 'sj3-conversion-backend
43           '(egg-start-conversion          sj3-start-conversion
44             egg-get-bunsetsu-source       sj3-get-bunsetsu-source
45             egg-get-bunsetsu-converted    sj3-get-bunsetsu-converted
46             egg-get-source-language       sj3-get-source-language
47             egg-get-converted-language    sj3-get-converted-language
48             egg-list-candidates           sj3-list-candidates
49             egg-decide-candidate          sj3-decide-candidate
50             egg-change-bunsetsu-length    sj3-change-bunsetsu-length
51             egg-end-conversion            sj3-end-conversion))
52
53 (defconst sj3-backend-alist '((Japanese ((sj3-conversion-backend)))))
54
55 (egg-set-finalize-backend '(sj3-finalize-backend))
56
57 (defvar sj3-server-port 3086 "Port number of SJ3 server")
58 (defvar sj3-stdy-size 0 "STDYSIZE of SJ3 server")
59 (defvar sj3-hostname "localhost"
60   "Hostname of SJ3 server")
61
62 (defvar sj3-open-message)
63
64 (defun sj3-open (hostname)
65   "Establish the connection to SJ3 server.  Return process object."
66   (let* ((buf (generate-new-buffer " *SJ3*"))
67          proc result)
68     (condition-case err
69         (setq proc (open-network-stream "SJ3" buf hostname sj3-server-port))
70       ((error quit)
71        (egg-error "failed to connect sj3 server")))
72     (process-kill-without-query proc)
73     (set-process-coding-system proc 'no-conversion 'no-conversion)
74     (set-marker-insertion-type (process-mark proc) t)
75     (save-excursion
76       (set-buffer buf)
77       (erase-buffer)
78       (buffer-disable-undo)
79       (set-buffer-multibyte nil))
80     ;; Initialize dictionaries
81     (setq sj3-sys-dict-list nil)
82     (setq sj3-user-dict-list nil)
83     (setq result (sj3rpc-open proc (system-name) (user-login-name)))
84     (if (< result 0)
85         (let ((msg (sj3rpc-get-error-message (- result))))
86           (delete-process proc)
87           (kill-buffer buf)
88           (egg-error "Can't open SJ3 session (%s): %s" hostname msg)))
89     (setq result (sj3rpc-get-stdy-size proc))
90     (if (< result 0)
91         (let ((msg (sj3rpc-get-error-message (- result))))
92           (delete-process proc)
93           (kill-buffer buf)
94           (egg-error "Can't get SJ3 STDYSIZE: %s"msg)))
95     (setq sj3-stdy-size result)
96     proc))
97
98 ;; (defun sj3-open (hostname-list)
99 ;;   "Establish the connection to SJ3 server.  Return process object."
100 ;;   (let* ((buf (generate-new-buffer " *SJ3*"))
101 ;;       (msg-form "SJ3: connecting to sj3serv at %s...")
102 ;;       hostname proc result msg)
103 ;;     (save-excursion
104 ;;       (set-buffer buf)
105 ;;       (erase-buffer)
106 ;;       (buffer-disable-undo)
107 ;;       (setq enable-multibyte-characters nil))
108 ;;     (cond
109 ;;      ((null hostname-list)
110 ;;       (setq hostname-list '("localhost")))
111 ;;      ((null (listp hostname-list))
112 ;;       (setq hostname-list (list hostname-list))))
113 ;;     (while (and hostname-list (null proc))
114 ;;       (setq hostname (car hostname-list)
115 ;;          hostname-list (cdr hostname-list))
116 ;;       (message msg-form hostname)
117 ;;       (sit-for 0)
118 ;;       (condition-case result
119 ;;        (setq proc (open-network-stream "SJ3" buf hostname sj3-server-port))
120 ;;      (error nil))
121 ;;       (if proc
122 ;;        (progn
123 ;;          (process-kill-without-query proc)
124 ;;          (set-process-coding-system proc 'no-conversion 'no-conversion)
125 ;;          (set-marker-insertion-type (process-mark proc) t)
126 ;;          ;; Initialize dictionaries
127 ;;          (setq sj3-sys-dict-list nil)
128 ;;          (setq sj3-user-dict-list nil)
129 ;;          (setq result (sj3rpc-open proc (system-name) (user-login-name)))
130 ;;          (if (< result 0)
131 ;;              (progn
132 ;;                (delete-process proc)
133 ;;                (setq proc nil
134 ;;                      msg (format "Can't open SJ3 session (%s): %s"
135 ;;                                  hostname msg)))
136 ;;            (setq result (sj3rpc-get-stdy-size proc))
137 ;;            (if (< result 0)
138 ;;                (progn
139 ;;                  (delete-process proc)
140 ;;                  (setq proc nil
141 ;;                        msg (format "Can't get SJ3 STDYSIZE: %s"
142 ;;                                    (sj3rpc-get-error-message (- result)))))
143 ;;              (setq sj3-stdy-size result))))))
144 ;;     (if proc
145 ;;      (progn
146 ;;        (setq sj3-open-message (format (concat msg-form "done") hostname))
147 ;;        proc)
148 ;;       (kill-buffer buf)
149 ;;       (error "%s" (or msg "no sj3serv available")))))
150
151 ;; <env> ::= [ <proc> <dictionary-list> ]
152 (defvar sj3-environment nil
153   "Environment for SJ3 kana-kanji conversion")
154
155 (defsubst sj3env-get-proc (env)
156   (aref env 0))
157 (defsubst sj3env-get-dictionary-list (env)
158   (aref env 1))
159
160 ;; <sj3-bunsetsu> ::=
161 ;;  [ <env> <source> <converted> <rest> <stdy>
162 ;;    <zenkouho> <zenkouho-pos> <zenkouho-converted>
163 ;;    <kugiri-changed> ]
164 (defsubst sj3-make-bunsetsu (env source converted rest stdy)
165   (egg-bunsetsu-create
166    'sj3-conversion-backend
167    (vector env source converted rest stdy nil nil nil nil nil)))
168
169 (defsubst sj3bunsetsu-get-env (b)
170   (aref (egg-bunsetsu-get-info b) 0))
171 (defsubst sj3bunsetsu-get-source (b)
172   (aref (egg-bunsetsu-get-info b) 1))
173 (defsubst sj3bunsetsu-get-converted (b)
174   (aref (egg-bunsetsu-get-info b) 2))
175 (defsubst sj3bunsetsu-get-rest (b)
176   (aref (egg-bunsetsu-get-info b) 3))
177 (defsubst sj3bunsetsu-get-stdy (b)
178   (aref (egg-bunsetsu-get-info b) 4))
179
180 (defsubst sj3bunsetsu-get-zenkouho (b)
181   (aref (egg-bunsetsu-get-info b) 5))
182 (defsubst sj3bunsetsu-set-zenkouho (b z)
183   (aset (egg-bunsetsu-get-info b) 5 z))
184
185 (defsubst sj3bunsetsu-get-zenkouho-pos (b)
186   (aref (egg-bunsetsu-get-info b) 6))
187 (defsubst sj3bunsetsu-set-zenkouho-pos (b zp)
188   (aset (egg-bunsetsu-get-info b) 6 zp))
189
190 (defsubst sj3bunsetsu-get-zenkouho-converted (b)
191   (aref (egg-bunsetsu-get-info b) 7))
192 (defsubst sj3bunsetsu-set-zenkouho-converted (b zc)
193   (aset (egg-bunsetsu-get-info b) 7 zc))
194
195 (defsubst sj3bunsetsu-get-kugiri-changed (b)
196   (aref (egg-bunsetsu-get-info b) 8))
197 (defsubst sj3bunsetsu-set-kugiri-changed (b s)
198   (aset (egg-bunsetsu-get-info b) 8 s))
199
200 (defun sj3-get-bunsetsu-source (b)
201   (sj3bunsetsu-get-source b))
202
203 (defun sj3-get-bunsetsu-converted (b)
204   (concat (sj3bunsetsu-get-converted b) (sj3bunsetsu-get-rest b)))
205
206 (defun sj3-get-source-language (b) 'Japanese)
207 (defun sj3-get-converted-language (b) 'Japanese)
208 (defun sj3-get-bunsetsu-stdy (b) (sj3bunsetsu-get-stdy b))
209
210 (defvar sj3-dictionary-specification
211   '(("study.dat")
212     ["sj3main.dic" ""]
213     [("private.dic") ""])
214   "Dictionary specification of SJ3.")
215
216 (defvar sj3-usr-dic-dir (concat "user/" (user-login-name))
217   "*Directory of user dictionary for SJ3.")
218
219 (defun sj3-filename (p)
220   ""
221   (cond ((consp p) (concat sj3-usr-dic-dir "/" (car p)))
222         (t p)))
223
224 (defun sj3-get-environment ()
225   "Return the backend of SJ3 environment."
226   (if sj3-environment
227       sj3-environment
228     (let* ((proc (sj3-open sj3-hostname))
229            (freq-info-name (sj3-filename (car sj3-dictionary-specification)))
230            (l (cdr sj3-dictionary-specification))
231            dict-list)
232       (sj3-open-freq-info proc freq-info-name)
233       (while l
234         (let ((dic (car l))
235               dic-id)
236           (setq dic-id
237                 (sj3-open-dictionary proc (sj3-filename (aref dic 0))
238                                      (aref dic 1)))
239           (if (< dic-id 0)
240               (egg-error "Dame2")       ; XXX
241             (setq dict-list (cons dic-id dict-list)
242                   l (cdr l)))))
243       (setq sj3-environment (vector proc dict-list)))))
244
245 (defun sj3-open-freq-info (proc name)
246   (let ((trying t)
247         ret)
248     (while trying
249       (setq ret (sj3rpc-open-stdy proc name))
250       (if (= ret 0)
251           (setq trying nil)
252         (message "\e$B3X=,%U%!%$%k\e(B(%s)\e$B$,$"$j$^$;$s\e(B" name)
253         (if (/= ret (SJ3-const FileNotExist))
254             (egg-error "Fatal1")        ; XXX
255           (if (and (y-or-n-p
256                     (format "\e$B3X=,%U%!%$%k\e(B(%s)\e$B$,$"$j$^$;$s!#:n$j$^$9$+\e(B? "
257                             name))
258                    (sj3rpc-make-directory proc
259                                           (file-name-directory name))
260                    ;; ignore error
261                    (= (sj3rpc-make-stdy proc name) 0))
262               (message "\e$B3X=,%U%!%$%k\e(B(%s)\e$B$r:n$j$^$7$?\e(B" name)
263             (egg-error "Fatal2")))))))  ; XXX
264
265 (defun sj3-open-dictionary (proc name passwd)
266   (let ((trying t)
267         ret)
268     (while trying
269       (setq ret (sj3rpc-open-dictionary proc name passwd))
270       (if (>= ret 0)
271           (setq trying nil)
272         (message "\e$B<-=q%U%!%$%k\e(B(%s)\e$B$,$"$j$^$;$s\e(B" name)
273         (setq ret (- ret))              ; Get error code.
274         (if (/= ret (SJ3-const FileNotExist))
275             (egg-error "Fatal3 %d" ret) ; XXX
276           (if (and (y-or-n-p
277                     (format "\e$B<-=q%U%!%$%k\e(B(%s)\e$B$,$"$j$^$;$s!#:n$j$^$9$+\e(B? "
278                             name))
279                    (= (sj3rpc-make-dictionary proc name) 0))
280               (message "\e$B<-=q%U%!%$%k\e(B(%s)\e$B$r:n$j$^$7$?\e(B" name)
281             (egg-error "Fatal4")))))    ; XXX
282     ret))
283
284 (defun sj3-start-conversion (backend yomi &optional context)
285   "Convert YOMI string to kanji, and enter conversion mode.
286 Return the list of bunsetsu."
287   (let ((env (sj3-get-environment)))
288     (sj3rpc-begin env yomi)))
289
290 (defun sj3-end-conversion (bunsetsu-list abort)
291   (if abort
292       ()
293     (let ((env (sj3bunsetsu-get-env (car bunsetsu-list)))
294           (l bunsetsu-list)
295           bunsetsu stdy kugiri-changed)
296       (while l
297         (setq bunsetsu (car l))
298         (setq l (cdr l))
299         (setq stdy (sj3bunsetsu-get-stdy bunsetsu))
300         (if stdy
301             (sj3rpc-bunsetsu-stdy env stdy))
302         (if (setq kugiri-changed (sj3bunsetsu-get-kugiri-changed bunsetsu))
303             (let ((yomi1 (sj3bunsetsu-get-source bunsetsu))
304                   (yomi2 (sj3bunsetsu-get-source (car l))))
305               (if (/= kugiri-changed (length yomi1))
306                   (sj3rpc-kugiri-stdy env yomi1 yomi2
307                                       (sj3bunsetsu-get-stdy (car l))))))))))
308
309 (defun sj3-list-candidates (bunsetsu prev-bunsetsu next-bunsetsu major)
310   (setq bunsetsu (car bunsetsu))
311   (if (sj3bunsetsu-get-zenkouho bunsetsu)
312       (cons (sj3bunsetsu-get-zenkouho-pos bunsetsu)
313             (sj3bunsetsu-get-zenkouho-converted bunsetsu))
314     (let* ((env (sj3bunsetsu-get-env bunsetsu))
315            (yomi (sj3bunsetsu-get-source bunsetsu))
316            (z (sj3rpc-get-bunsetsu-candidates env yomi)))
317       (sj3bunsetsu-set-zenkouho bunsetsu z)
318       (cons (sj3bunsetsu-set-zenkouho-pos bunsetsu 0)
319             (sj3bunsetsu-set-zenkouho-converted
320              bunsetsu
321              (mapcar 'sj3bunsetsu-get-converted z))))))
322
323 (defun sj3-decide-candidate (bunsetsu candidate-pos prev-b next-b)
324   (setq bunsetsu (car bunsetsu))
325   (let* ((candidate-list (sj3bunsetsu-get-zenkouho bunsetsu))
326          (candidate (nth candidate-pos candidate-list)))
327     (sj3bunsetsu-set-zenkouho candidate candidate-list)
328     (sj3bunsetsu-set-zenkouho-pos candidate candidate-pos)
329     (sj3bunsetsu-set-zenkouho-converted
330      candidate (sj3bunsetsu-get-zenkouho-converted bunsetsu))
331     (list (list candidate))))
332
333 (defun sj3-change-bunsetsu-length (bunsetsu prev-b next-b len major)
334   (let ((yomi (apply 'concat (mapcar 'sj3bunsetsu-get-source bunsetsu)))
335         (env (sj3bunsetsu-get-env (car bunsetsu)))
336         (old (car bunsetsu))
337         new yomi1 yomi2)
338     (setq yomi1 (substring yomi 0 len)
339           yomi2 (substring yomi len))
340     (setq new (sj3rpc-tanbunsetsu-conversion env yomi1))
341     ;; Only set once (memory original length of the bunsetsu).
342     (sj3bunsetsu-set-kugiri-changed new 
343                                     (or (sj3bunsetsu-get-kugiri-changed old)
344                                         (length (sj3bunsetsu-get-source old))))
345     (if (> (length yomi2) 0)
346         (list (list new (sj3rpc-tanbunsetsu-conversion env yomi2)))
347       (list (list new)))))
348
349 (defun sj3-finalize-backend ()
350   (if sj3-environment
351       (let ((proc (sj3env-get-proc sj3-environment))
352             (dict-list (sj3env-get-dictionary-list sj3-environment))
353             dict)
354         (while dict-list
355           (setq dict (car dict-list))
356           (setq dict-list (cdr dict-list))
357           (sj3rpc-close-dictionary proc dict)) ; XXX: check error
358         (sj3rpc-close-stdy proc)
359         (sj3rpc-close proc)
360         (setq sj3-environment nil))))
361
362 ;;; setup
363
364 (load "egg/sj3rpc")
365 (run-hooks 'sj3-load-hook)
366
367 ;;;###autoload
368 (defun egg-activate-sj3 (&rest arg)
369   "Activate SJ3 backend of Tamagotchy."
370   (apply 'egg-mode (append arg sj3-backend-alist)))
371
372 ;;; egg/sj3.el ends here.