Fix FSF address in comment.
[bpt/emacs.git] / lisp / international / skkdic-cnv.el
CommitLineData
4ed46869
KH
1;; skkdic-cnv.el -- convert a SKK dictionary for `skkdic-utl'
2
3;; Copyright (C) 1995 Free Software Foundation, Inc.
4;; Copyright (C) 1995 Electrotechnical Laboratory, JAPAN.
5
6;; Keywords: mule, multilingual, Japanese, SKK
7
8;; This file is part of GNU Emacs.
9
10;; GNU Emacs is free software; you can redistribute it and/or modify
11;; it under the terms of the GNU General Public License as published by
12;; the Free Software Foundation; either version 2, or (at your option)
13;; any later version.
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
369314dc
KH
21;; along with GNU Emacs; see the file COPYING. If not, write to the
22;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23;; Boston, MA 02111-1307, USA.
4ed46869
KH
24
25;;; Commentary:
26
27;; SKK is a Japanese input method running on Mule created by Masahiko
28;; Sato <masahiko@sato.riec.tohoku.ac.jp>. Here we provide utilities
29;; to handle a dictionary distributed with SKK so that a different
30;; input method (e.g. quail-japanese) can utilize the dictionary.
31
32;; The format of SKK dictionary is quite simple. Each line has the
33;; form "KANASTRING /CONV1/CONV2/.../" which means KANASTRING (\e$B2>L>J8\e(B
34;; \e$B;zNs\e(B) can be converted to one of CONVi. CONVi is a Kanji (\e$B4A;z\e(B)
35;; and Kana (\e$B2>L>\e(B) mixed string.
36;;
37;; KANASTRING may have a trailing ASCII letter for Okurigana (\e$BAw$j2>L>\e(B)
38;; information. For instance, the trailing letter `k' means that one
39;; of the following Okurigana is allowed: \e$B$+$-$/$1$3\e(B. So, in that
40;; case, the string "KANASTRING\e$B$/\e(B" can be converted to one of "CONV1\e$B$/\e(B",
41;; CONV2\e$B$/\e(B, ...
42
43;;; Code:
44
45;; Name of a file to generate from SKK dictionary.
46(defvar skkdic-filename "skkdic.el")
47
48;; To make a generated skkdic.el smaller.
49(make-coding-system
50 'coding-system-iso-2022-7-short
51 2 ?J
52 "Like `coding-system-iso-2022' but no ASCII designation before SPC."
53 '(ascii nil nil nil t t nil t))
54
55(defconst skkdic-jbytes
56 (charset-bytes 'japanese-jisx0208))
57
58(defun skkdic-convert-okuri-ari (skkbuf buf)
59 (message "Processing OKURI-ARI entries ...")
60 (goto-char (point-min))
61 (save-excursion
62 (set-buffer buf)
63 (insert ";; Setting okuri-ari entries.\n"
64 "(skkdic-set-okuri-ari\n"))
65 (while (not (eobp))
66 (let ((from (point))
67 to)
68 (end-of-line)
69 (setq to (point))
70
71 (save-excursion
72 (set-buffer buf)
73 (insert-buffer-substring skkbuf from to)
74 (beginning-of-line)
75 (insert "\"")
76 (search-forward " ")
77 (delete-char 1) ; delete the first '/'
78 (let ((p (point)))
79 (end-of-line)
80 (delete-char -1) ; delete the last '/'
81 (subst-char-in-region p (point) ?/ ? 'noundo))
82 (insert "\"\n"))
83
84 (forward-line 1)))
85 (save-excursion
86 (set-buffer buf)
87 (insert ")\n\n")))
88
89(defconst skkdic-postfix-list '(skkdic-postfix-list))
90
91(defconst skkdic-postfix-data
92 '(("\e$B$$$-\e(B" "\e$B9T\e(B")
93 ("\e$B$,$+$j\e(B" "\e$B78\e(B")
94 ("\e$B$,$/\e(B" "\e$B3X\e(B")
95 ("\e$B$,$o\e(B" "\e$B@n\e(B")
96 ("\e$B$7$c\e(B" "\e$B<R\e(B")
97 ("\e$B$7$e$&\e(B" "\e$B=8\e(B")
98 ("\e$B$7$g$&\e(B" "\e$B>^\e(B" "\e$B>k\e(B")
99 ("\e$B$8$g$&\e(B" "\e$B>k\e(B")
100 ("\e$B$;$s\e(B" "\e$B@~\e(B")
101 ("\e$B$@$1\e(B" "\e$B3Y\e(B")
102 ("\e$B$A$c$/\e(B" "\e$BCe\e(B")
103 ("\e$B$F$s\e(B" "\e$BE9\e(B")
104 ("\e$B$H$&$2\e(B" "\e$BF=\e(B")
105 ("\e$B$I$*$j\e(B" "\e$BDL$j\e(B")
106 ("\e$B$d$^\e(B" "\e$B;3\e(B")
107 ("\e$B$P$7\e(B" "\e$B66\e(B")
108 ("\e$B$O$D\e(B" "\e$BH/\e(B")
109 ("\e$B$b$/\e(B" "\e$BL\\e(B")
110 ("\e$B$f$-\e(B" "\e$B9T\e(B")))
111
112(defun skkdic-convert-postfix (skkbuf buf)
113 (message "Processing POSTFIX entries ...")
114 (goto-char (point-min))
115 (save-excursion
116 (set-buffer buf)
117 (insert ";; Setting postfix entries.\n"
118 "(skkdic-set-postfix\n"))
119
120 ;; Initialize SKKDIC-POSTFIX-LIST by predefined data
121 ;; SKKDIC-POSTFIX-DATA.
122 (save-excursion
123 (set-buffer buf)
124 (let ((l skkdic-postfix-data)
125 kana candidates entry)
126 (while l
127 (setq kana (car (car l)) candidates (cdr (car l)))
128 (insert "\"" kana)
129 (while candidates
130 (insert " " (car candidates))
131 (setq entry (lookup-nested-alist (car candidates)
132 skkdic-postfix-list nil nil t))
133 (if (consp (car entry))
134 (setcar entry (cons kana (car entry)))
135 (set-nested-alist (car candidates) (list kana)
136 skkdic-postfix-list))
137 (setq candidates (cdr candidates)))
138 (insert "\"\n")
139 (setq l (cdr l)))))
140
141 ;; Search postfix entries.
142 (while (re-search-forward "^[#<>?]\\(\\cH+\\) " nil t)
143 (let ((kana (match-string 1))
144 str candidates)
145 (while (looking-at "/[#0-9 ]*\\([^/\n]*\\)/")
146 (setq str (match-string 1))
147 (if (not (member str candidates))
148 (setq candidates (cons str candidates)))
149 (goto-char (match-end 1)))
150 (save-excursion
151 (set-buffer buf)
152 (insert "\"" kana)
153 (while candidates
154 (insert " " (car candidates))
155 (let ((entry (lookup-nested-alist (car candidates)
156 skkdic-postfix-list nil nil t)))
157 (if (consp (car entry))
158 (if (not (member kana (car entry)))
159 (setcar entry (cons kana (car entry))))
160 (set-nested-alist (car candidates) (list kana)
161 skkdic-postfix-list)))
162 (setq candidates (cdr candidates)))
163 (insert "\"\n"))))
164 (save-excursion
165 (set-buffer buf)
166 (insert ")\n\n")))
167
168(defconst skkdic-prefix-list '(skkdic-prefix-list))
169
170(defun skkdic-convert-prefix (skkbuf buf)
171 (message "Processing PREFIX entries ...")
172 (goto-char (point-min))
173 (save-excursion
174 (set-buffer buf)
175 (insert ";; Setting prefix entries.\n"
176 "(skkdic-set-prefix\n"))
177 (save-excursion
178 (while (re-search-forward "^\\(\\cH+\\)[<>?] " nil t)
179 (let ((kana (match-string 1))
180 str candidates)
181 (while (looking-at "/\\([^/\n]+\\)/")
182 (setq str (match-string 1))
183 (if (not (member str candidates))
184 (setq candidates (cons str candidates)))
185 (goto-char (match-end 1)))
186 (save-excursion
187 (set-buffer buf)
188 (insert "\"" kana)
189 (while candidates
190 (insert " " (car candidates))
191 (set-nested-alist (car candidates) kana skkdic-prefix-list)
192 (setq candidates (cdr candidates)))
193 (insert "\"\n")))))
194 (save-excursion
195 (set-buffer buf)
196 (insert ")\n\n")))
197
198;; FROM and TO point the head and tail of "/J../J../.../".
199(defun skkdic-get-candidate-list (from to)
200 (let (candidates)
201 (goto-char from)
202 (while (re-search-forward "/\\cj+" to t)
203 (setq candidates (cons (buffer-substring (1+ (match-beginning 0))
204 (match-end 0))
205 candidates)))
206 candidates))
207
208;; Return entry for STR from nested alist ALIST.
209(defsubst skkdic-get-entry (str alist)
210 (car (lookup-nested-alist str alist nil nil t)))
211
212
213(defconst skkdic-word-list '(skkdic-word-list))
214
215;; Return t if substring of STR (between FROM and TO) can be broken up
216;; to chunks all of which can be derived from another entry in SKK
217;; dictionary. SKKBUF is the buffer where the original SKK dictionary
218;; is visited, KANA is the current entry for STR. FIRST is t iff this
219;; is called at top level.
220
221(defun skkdic-breakup-string (skkbuf kana str from to &optional first)
222 (let ((len (- to from)))
223 (or (and (>= len (* skkdic-jbytes 2))
224 (let ((min-idx (+ from (* skkdic-jbytes 2)))
225 (idx (if first (- to skkdic-jbytes) to))
226 (found nil))
227 (while (and (not found) (>= idx min-idx))
228 (let ((kana2-list (skkdic-get-entry
229 (substring str from idx)
230 skkdic-word-list)))
231 (if (or (and (consp kana2-list)
232 (let ((kana-len (length kana))
233 kana2)
234 (catch 'skkdic-tag
235 (while kana2-list
236 (setq kana2 (car kana2-list))
237 (if (string-match kana2 kana)
238 (throw 'skkdic-tag t))
239 (setq kana2-list (cdr kana2-list)))))
240 (or (= idx to)
241 (skkdic-breakup-string skkbuf kana str
242 idx to)))
243 (and (stringp kana2-list)
244 (string-match kana2-list kana)))
245 (setq found t)
246 (setq idx (- idx skkdic-jbytes)))))
247 found))
248 (and first
249 (> len (* skkdic-jbytes 2))
250 (let ((kana2 (skkdic-get-entry
251 (substring str from (+ from skkdic-jbytes))
252 skkdic-prefix-list)))
253 (and (stringp kana2)
254 (eq (string-match kana2 kana) 0)))
255 (skkdic-breakup-string skkbuf kana str (+ from skkdic-jbytes) to))
256 (and (not first)
257 (>= len skkdic-jbytes)
258 (let ((kana2-list (skkdic-get-entry
259 (substring str from to)
260 skkdic-postfix-list)))
261 (and (consp kana2-list)
262 (let (kana2)
263 (catch 'skkdic-tag
264 (while kana2-list
265 (setq kana2 (car kana2-list))
266 (if (string= kana2
267 (substring kana (- (length kana2))))
268 (throw 'skkdic-tag t))
269 (setq kana2-list (cdr kana2-list)))))))))))
270
271;; Return list of candidates which excludes some from CANDIDATES.
272;; Excluded candidates can be derived from another entry.
273
274(defun skkdic-reduced-candidates (skkbuf kana candidates)
275 (let (elt l)
276 (while candidates
277 (setq elt (car candidates))
278 (if (or (= (length elt) skkdic-jbytes)
279 (and (string-match "^\\cj" elt)
280 (not (skkdic-breakup-string skkbuf kana elt 0 (length elt)
281 'first))))
282 (setq l (cons elt l)))
283 (setq candidates (cdr candidates)))
284 (nreverse l)))
285
286(defconst skkdic-okuri-nasi-entries (list nil))
287(defconst skkdic-okuri-nasi-entries-count 0)
288
289(defun skkdic-collect-okuri-nasi ()
290 (message "Collecting OKURI-NASI entries ...")
291 (save-excursion
292 (let ((prev-ratio 0)
293 ratio)
294 (while (re-search-forward "^\\(\\cH+\\) \\(/\\cj.*\\)/$" nil t)
295 (let ((kana (match-string 1))
296 (candidates (skkdic-get-candidate-list (match-beginning 2)
297 (match-end 2))))
298 (setq skkdic-okuri-nasi-entries
299 (cons (cons kana candidates) skkdic-okuri-nasi-entries)
300 skkdic-okuri-nasi-entries-count
301 (1+ skkdic-okuri-nasi-entries-count))
302 (setq ratio (floor (/ (* (point) 100.0) (point-max))))
303 (if (/= ratio prev-ratio)
304 (progn
305 (message "collected %2d%% %s ..." ratio kana)
306 (setq prev-ratio ratio)))
307 (while candidates
308 (let ((entry (lookup-nested-alist (car candidates)
309 skkdic-word-list nil nil t)))
310 (if (consp (car entry))
311 (setcar entry (cons kana (car entry)))
312 (set-nested-alist (car candidates) (list kana)
313 skkdic-word-list)))
314 (setq candidates (cdr candidates))))))))
315
316(defun skkdic-convert-okuri-nasi (skkbuf buf)
317 (message "Processing OKURI-NASI entries ...")
318 (save-excursion
319 (set-buffer buf)
320 (insert ";; Setting okuri-nasi entries.\n"
321 "(skkdic-set-okuri-nasi\n")
322 (let ((l (nreverse skkdic-okuri-nasi-entries))
323 (count 0)
324 (prev-ratio 0)
325 ratio)
326 (while l
327 (let ((kana (car (car l)))
328 (candidates (cdr (car l))))
329 (setq ratio (/ (* count 1000) skkdic-okuri-nasi-entries-count)
330 count (1+ count))
331 (if (/= prev-ratio (/ ratio 10))
332 (progn
333 (message "processed %2d%% %s ..." (/ ratio 10) kana)
334 (setq prev-ratio (/ ratio 10))))
335 (if (setq candidates
336 (skkdic-reduced-candidates skkbuf kana candidates))
337 (progn
338 (insert "\"" kana)
339 (while candidates
340 (insert " " (car candidates))
341 (setq candidates (cdr candidates)))
342 (insert "\"\n"))))
343 (setq l (cdr l))))
344 (insert ")\n\n")))
345
346(defun skkdic-convert (filename &optional dirname)
347 "Convert SKK dictionary of FILENAME into the file \"skkdic.el\".
348Optional argument DIRNAME if specified is the directory name under which
349the generated \"skkdic.el\" is saved."
350 (interactive "FSKK dictionary file: ")
351 (message "Reading file \"%s\" ..." filename)
352 (let ((skkbuf(find-file-noselect (expand-file-name filename)))
353 (buf (get-buffer-create "*skkdic-work*")))
354 (save-excursion
355 ;; Setup and generate the header part of working buffer.
356 (set-buffer buf)
357 (erase-buffer)
358 (buffer-disable-undo)
359 (insert ";; skkdic.el -- dictionary for Japanese input method\n"
360 ";;\tGenerated by the command `skkdic-convert'\n"
361 ";;\tDate: " (current-time-string) "\n"
362 ";;\tOriginal SKK dictionary file: "
363 (file-name-nondirectory filename)
364 "\n\n"
365 ";;; Comment:\n\n"
366 ";; Do byte-compile this file again after any modification.\n\n"
367 ";;; Start of the header of the original TIT dictionary.\n\n")
368 (set-buffer skkbuf)
369 (widen)
370 (goto-char 1)
371 (let (pos)
372 (search-forward ";; okuri-ari")
373 (forward-line 1)
374 (setq pos (point))
375 (set-buffer buf)
376 (insert-buffer-substring skkbuf 1 pos))
377 (insert "\n"
378 ";;; Code:\n\n")
379
380 ;; Generate the body part of working buffer.
381 (set-buffer skkbuf)
382 (let ((from (point))
383 to)
384 ;; Convert okuri-ari entries.
385 (search-forward ";; okuri-nasi")
386 (beginning-of-line)
387 (setq to (point))
388 (narrow-to-region from to)
389 (skkdic-convert-okuri-ari skkbuf buf)
390 (widen)
391
392 ;; Convert okuri-nasi postfix entries.
393 (goto-char to)
394 (forward-line 1)
395 (setq from (point))
396 (re-search-forward "^\\cH")
397 (setq to (match-beginning 0))
398 (narrow-to-region from to)
399 (skkdic-convert-postfix skkbuf buf)
400 (widen)
401
402 ;; Convert okuri-nasi prefix entries.
403 (goto-char to)
404 (skkdic-convert-prefix skkbuf buf)
405
406 ;;
407 (skkdic-collect-okuri-nasi)
408
409 ;; Convert okuri-nasi general entries.
410 (skkdic-convert-okuri-nasi skkbuf buf)
411
412 ;; Postfix
413 (save-excursion
414 (set-buffer buf)
415 (goto-char (point-max))
416 (insert ";;\n(provide 'skkdic)\n\n;; skkdic.el ends here\n")))
417
418 ;; Save the working buffer.
419 (set-buffer buf)
420 (set-visited-file-name (expand-file-name skkdic-filename dirname) t)
421 (set-buffer-file-coding-system 'coding-system-iso-2022-7-short)
422 (save-buffer 0))
423 (kill-buffer skkbuf)
424 (switch-to-buffer buf)))
425
426(defun batch-skkdic-convert ()
427 "Run `skkdic-convert' on the files remaining on the command line.
428Use this from the command line, with `-batch';
429it won't work in an interactive Emacs.
430For example, invoke:
431 % emacs -batch -l skkconv -f batch-skkdic-convert SKK-JISYO.L
432to generate \"skkdic.el\" from SKK dictionary file \"SKK-JISYO.L\".
433To get complete usage, invoke:
434 % emacs -batch -l skkconv -f batch-skkdic-convert -h"
435 (defvar command-line-args-left) ; Avoid compiler warning.
436 (if (not noninteractive)
437 (error "`batch-skkdic-convert' should be used only with -batch"))
438 (if (string= (car command-line-args-left) "-h")
439 (progn
440 (message "To convert SKK-JISYO.L into skkdic.el:")
441 (message " %% emacs -batch -l skkdic-conv -f batch-skkdic-convert SKK-JISYO.L")
442 (message "To convert SKK-JISYO.L into DIR/skkdic.el:")
443 (message " %% emacs -batch -l skkdic-conv -f batch-skkdic-convert -dir DIR SKK-JISYO.L"))
444 (let (targetdir filename)
445 (if (string= (car command-line-args-left) "-dir")
446 (progn
447 (setq command-line-args-left (cdr command-line-args-left))
448 (setq targetdir (expand-file-name (car command-line-args-left)))
449 (setq command-line-args-left (cdr command-line-args-left))))
450 (setq filename (expand-file-name (car command-line-args-left)))
451 (message "Converting %s to skkdic.el ..." filename)
452 (message "It takes around 10 minutes even on Sun SS20.")
453 (skkdic-convert filename targetdir)
454 (message "Do byte-compile the created file by:")
455 (message " %% emacs -batch -l skkdic-cnv -f batch-byte-compile skkdic.el")
456 (message " ^^^^^^^^^^^^^ -- Don't forget this option!")
457 ))
458 (kill-emacs 0))
459
460
461;; The following macros are expanded at byte-compiling time so that
462;; compiled code can be loaded quickly.
463
464(defun skkdic-get-kana-compact-codes (kana)
465 (let* ((len (length kana))
466 (vec (make-vector (/ (+ len (1- skkdic-jbytes)) skkdic-jbytes) 0))
467 (i 0)
468 ch)
469 (while (< i len)
470 (setq ch (sref kana i))
471 (aset vec (/ i 3)
472 (if (< ch 128) ; CH is an ASCII letter for OKURIGANA,
473 (- ch) ; represented by a negative code.
474 (if (= ch ?\e$B!<\e(B) ; `\e$B!<\e(B' is represented by 0.
475 0
476 (- (nth 2 (split-char ch)) 32))))
477 (setq i (+ i 3)))
478 vec))
479
480(defun skkdic-extract-conversion-data (entry)
481 (string-match "^\\cH+[a-z]* " entry)
482 (let ((kana (substring entry (match-beginning 0) (1- (match-end 0))))
483 (i (match-end 0))
484 candidates)
485 (while (string-match "[^ ]+" entry i)
486 (setq candidates (cons (match-string 0 entry) candidates))
487 (setq i (match-end 0)))
488 (cons (skkdic-get-kana-compact-codes kana) candidates)))
489
490(defmacro skkdic-set-okuri-ari (&rest entries)
491 `(defconst skkdic-okuri-ari
492 ',(let ((l entries)
493 (map '(skkdic-okuri-ari))
494 entry)
495 (while l
496 (setq entry (skkdic-extract-conversion-data (car l)))
497 (set-nested-alist (car entry) (cdr entry) map)
498 (setq l (cdr l)))
499 map)))
500
501(defmacro skkdic-set-postfix (&rest entries)
502 `(defconst skkdic-postfix
503 ',(let ((l entries)
504 (map '(nil))
505 (longest 1)
506 len entry)
507 (while l
508 (setq entry (skkdic-extract-conversion-data (car l)))
509 (setq len (length (car entry)))
510 (if (> len longest)
511 (setq longest len))
512 (let ((entry2 (lookup-nested-alist (car entry) map nil nil t)))
513 (if (consp (car entry2))
514 (let ((conversions (cdr entry)))
515 (while conversions
516 (if (not (member (car conversions) (car entry2)))
517 (setcar entry2 (cons (car conversions) (car entry2))))
518 (setq conversions (cdr conversions))))
519 (set-nested-alist (car entry) (cdr entry) map)))
520 (setq l (cdr l)))
521 (setcar map longest)
522 map)))
523
524(defmacro skkdic-set-prefix (&rest entries)
525 `(defconst skkdic-prefix
526 ',(let ((l entries)
527 (map '(nil))
528 (longest 1)
529 len entry)
530 (while l
531 (setq entry (skkdic-extract-conversion-data (car l)))
532 (setq len (length (car entry)))
533 (if (> len longest)
534 (setq longest len))
535 (let ((entry2 (lookup-nested-alist (car entry) map len nil t)))
536 (if (consp (car entry2))
537 (let ((conversions (cdr entry)))
538 (while conversions
539 (if (not (member (car conversions) (car entry2)))
540 (setcar entry2 (cons (car conversions) (car entry2))))
541 (setq conversions (cdr conversions))))
542 (set-nested-alist (car entry) (cdr entry) map len)))
543 (setq l (cdr l)))
544 (setcar map longest)
545 map)))
546
547(defmacro skkdic-set-okuri-nasi (&rest entries)
548 `(defconst skkdic-okuri-nasi
549 ',(let ((l entries)
550 (map '(skdic-okuri-nasi))
551 (count 0)
552 entry)
553 (while l
554 (setq count (1+ count))
555 (if (= (% count 10) 0)
556 (message (format "%d entries" count)))
557 (setq entry (skkdic-extract-conversion-data (car l)))
558 (set-nested-alist (car entry) (cdr entry) map)
559 (setq l (cdr l)))
560 map)))
561
562;; skkdic-cnv.el ends here