1 ;;; skkdic-cnv.el --- Convert a SKK dictionary for `skkdic-utl'
3 ;; Copyright (C) 1995 Electrotechnical Laboratory, JAPAN.
4 ;; Licensed to the Free Software Foundation.
6 ;; Keywords: mule, multilingual, Japanese, SKK
8 ;; This file is part of GNU Emacs.
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)
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.
20 ;; You should have received a copy of the GNU General Public License
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.
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.
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.
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, ...
45 ;; Name of a file to generate from SKK dictionary.
46 (defvar skkdic-filename "skkdic.el")
48 ;; To make a generated skkdic.el smaller.
52 "Like `iso-2022-7bit' but no ASCII designation before SPC."
53 '(ascii nil nil nil t t nil t))
55 (defun skkdic-convert-okuri-ari (skkbuf buf)
56 (message "Processing OKURI-ARI entries ...")
57 (goto-char (point-min))
60 (insert ";; Setting okuri-ari entries.\n"
61 "(skkdic-set-okuri-ari\n"))
70 (insert-buffer-substring skkbuf from to)
74 (delete-char 1) ; delete the first '/'
77 (delete-char -1) ; delete the last '/'
78 (subst-char-in-region p (point) ?/ ? 'noundo))
86 (defconst skkdic-postfix-list '(skkdic-postfix-list))
88 (defconst skkdic-postfix-data
89 '(("
\e$B$$$-
\e(B" "
\e$B9T
\e(B")
90 ("
\e$B$,$+$j
\e(B" "
\e$B78
\e(B")
91 ("
\e$B$,$/
\e(B" "
\e$B3X
\e(B")
92 ("
\e$B$,$o
\e(B" "
\e$B@n
\e(B")
93 ("
\e$B$7$c
\e(B" "
\e$B<R
\e(B")
94 ("
\e$B$7$e$&
\e(B" "
\e$B=8
\e(B")
95 ("
\e$B$7$g$&
\e(B" "
\e$B>^
\e(B" "
\e$B>k
\e(B")
96 ("
\e$B$8$g$&
\e(B" "
\e$B>k
\e(B")
97 ("
\e$B$;$s
\e(B" "
\e$B@~
\e(B")
98 ("
\e$B$@$1
\e(B" "
\e$B3Y
\e(B")
99 ("
\e$B$A$c$/
\e(B" "
\e$BCe
\e(B")
100 ("
\e$B$F$s
\e(B" "
\e$BE9
\e(B")
101 ("
\e$B$H$&$2
\e(B" "
\e$BF=
\e(B")
102 ("
\e$B$I$*$j
\e(B" "
\e$BDL$j
\e(B")
103 ("
\e$B$d$^
\e(B" "
\e$B;3
\e(B")
104 ("
\e$B$P$7
\e(B" "
\e$B66
\e(B")
105 ("
\e$B$O$D
\e(B" "
\e$BH/
\e(B")
106 ("
\e$B$b$/
\e(B" "
\e$BL\
\e(B")
107 ("
\e$B$f$-
\e(B" "
\e$B9T
\e(B")))
109 (defun skkdic-convert-postfix (skkbuf buf)
110 (message "Processing POSTFIX entries ...")
111 (goto-char (point-min))
114 (insert ";; Setting postfix entries.\n"
115 "(skkdic-set-postfix\n"))
117 ;; Initialize SKKDIC-POSTFIX-LIST by predefined data
118 ;; SKKDIC-POSTFIX-DATA.
121 (let ((l skkdic-postfix-data)
122 kana candidates entry)
124 (setq kana (car (car l)) candidates (cdr (car l)))
127 (insert " " (car candidates))
128 (setq entry (lookup-nested-alist (car candidates)
129 skkdic-postfix-list nil nil t))
130 (if (consp (car entry))
131 (setcar entry (cons kana (car entry)))
132 (set-nested-alist (car candidates) (list kana)
133 skkdic-postfix-list))
134 (setq candidates (cdr candidates)))
138 ;; Search postfix entries.
139 (while (re-search-forward "^[#<>?]\\(\\cH+\\) " nil t)
140 (let ((kana (match-string 1))
142 (while (looking-at "/[#0-9 ]*\\([^/\n]*\\)/")
143 (setq str (match-string 1))
144 (if (not (member str candidates))
145 (setq candidates (cons str candidates)))
146 (goto-char (match-end 1)))
151 (insert " " (car candidates))
152 (let ((entry (lookup-nested-alist (car candidates)
153 skkdic-postfix-list nil nil t)))
154 (if (consp (car entry))
155 (if (not (member kana (car entry)))
156 (setcar entry (cons kana (car entry))))
157 (set-nested-alist (car candidates) (list kana)
158 skkdic-postfix-list)))
159 (setq candidates (cdr candidates)))
165 (defconst skkdic-prefix-list '(skkdic-prefix-list))
167 (defun skkdic-convert-prefix (skkbuf buf)
168 (message "Processing PREFIX entries ...")
169 (goto-char (point-min))
172 (insert ";; Setting prefix entries.\n"
173 "(skkdic-set-prefix\n"))
175 (while (re-search-forward "^\\(\\cH+\\)[<>?] " nil t)
176 (let ((kana (match-string 1))
178 (while (looking-at "/\\([^/\n]+\\)/")
179 (setq str (match-string 1))
180 (if (not (member str candidates))
181 (setq candidates (cons str candidates)))
182 (goto-char (match-end 1)))
187 (insert " " (car candidates))
188 (set-nested-alist (car candidates) kana skkdic-prefix-list)
189 (setq candidates (cdr candidates)))
195 ;; FROM and TO point the head and tail of "/J../J../.../".
196 (defun skkdic-get-candidate-list (from to)
199 (while (re-search-forward "/\\cj+" to t)
200 (setq candidates (cons (buffer-substring (1+ (match-beginning 0))
205 ;; Return entry for STR from nested alist ALIST.
206 (defsubst skkdic-get-entry (str alist)
207 (car (lookup-nested-alist str alist nil nil t)))
210 (defconst skkdic-word-list '(skkdic-word-list))
212 ;; Return t if substring of STR (between FROM and TO) can be broken up
213 ;; to chunks all of which can be derived from another entry in SKK
214 ;; dictionary. SKKBUF is the buffer where the original SKK dictionary
215 ;; is visited, KANA is the current entry for STR. FIRST is t iff this
216 ;; is called at top level.
218 (defun skkdic-breakup-string (skkbuf kana str from to &optional first)
219 (let ((len (- to from)))
221 (let ((min-idx (+ from 2))
222 (idx (if first (1- to ) to))
224 (while (and (not found) (>= idx min-idx))
225 (let ((kana2-list (skkdic-get-entry
226 (substring str from idx)
228 (if (or (and (consp kana2-list)
229 (let ((kana-len (length kana))
233 (setq kana2 (car kana2-list))
234 (if (string-match kana2 kana)
235 (throw 'skkdic-tag t))
236 (setq kana2-list (cdr kana2-list)))))
238 (skkdic-breakup-string skkbuf kana str
240 (and (stringp kana2-list)
241 (string-match kana2-list kana)))
243 (setq idx (1- idx)))))
247 (let ((kana2 (skkdic-get-entry
248 (substring str from (1+ from))
249 skkdic-prefix-list)))
251 (eq (string-match kana2 kana) 0)))
252 (skkdic-breakup-string skkbuf kana str (1+ from) to))
255 (let ((kana2-list (skkdic-get-entry
256 (substring str from to)
257 skkdic-postfix-list)))
258 (and (consp kana2-list)
262 (setq kana2 (car kana2-list))
264 (substring kana (- (length kana2))))
265 (throw 'skkdic-tag t))
266 (setq kana2-list (cdr kana2-list)))))))))))
268 ;; Return list of candidates which excludes some from CANDIDATES.
269 ;; Excluded candidates can be derived from another entry.
271 (defun skkdic-reduced-candidates (skkbuf kana candidates)
274 (setq elt (car candidates))
275 (if (or (= (length elt) 1)
276 (and (string-match "^\\cj" elt)
277 (not (skkdic-breakup-string skkbuf kana elt 0 (length elt)
279 (setq l (cons elt l)))
280 (setq candidates (cdr candidates)))
283 (defconst skkdic-okuri-nasi-entries (list nil))
284 (defconst skkdic-okuri-nasi-entries-count 0)
286 (defun skkdic-collect-okuri-nasi ()
287 (message "Collecting OKURI-NASI entries ...")
291 (while (re-search-forward "^\\(\\cH+\\) \\(/\\cj.*\\)/$" nil t)
292 (let ((kana (match-string 1))
293 (candidates (skkdic-get-candidate-list (match-beginning 2)
295 (setq skkdic-okuri-nasi-entries
296 (cons (cons kana candidates) skkdic-okuri-nasi-entries)
297 skkdic-okuri-nasi-entries-count
298 (1+ skkdic-okuri-nasi-entries-count))
299 (setq ratio (floor (/ (* (point) 100.0) (point-max))))
300 (if (/= ratio prev-ratio)
302 (message "collected %2d%% %s ..." ratio kana)
303 (setq prev-ratio ratio)))
305 (let ((entry (lookup-nested-alist (car candidates)
306 skkdic-word-list nil nil t)))
307 (if (consp (car entry))
308 (setcar entry (cons kana (car entry)))
309 (set-nested-alist (car candidates) (list kana)
311 (setq candidates (cdr candidates))))))))
313 (defun skkdic-convert-okuri-nasi (skkbuf buf)
314 (message "Processing OKURI-NASI entries ...")
317 (insert ";; Setting okuri-nasi entries.\n"
318 "(skkdic-set-okuri-nasi\n")
319 (let ((l (nreverse skkdic-okuri-nasi-entries))
324 (let ((kana (car (car l)))
325 (candidates (cdr (car l))))
326 (setq ratio (/ (* count 1000) skkdic-okuri-nasi-entries-count)
328 (if (/= prev-ratio (/ ratio 10))
330 (message "processed %2d%% %s ..." (/ ratio 10) kana)
331 (setq prev-ratio (/ ratio 10))))
333 (skkdic-reduced-candidates skkbuf kana candidates))
337 (insert " " (car candidates))
338 (setq candidates (cdr candidates)))
343 (defun skkdic-convert (filename &optional dirname)
344 "Convert SKK dictionary of FILENAME into the file \"skkdic.el\".
345 Optional argument DIRNAME if specified is the directory name under which
346 the generated \"skkdic.el\" is saved."
347 (interactive "FSKK dictionary file: ")
348 (message "Reading file \"%s\" ..." filename)
349 (let ((skkbuf(find-file-noselect (expand-file-name filename)))
350 (buf (get-buffer-create "*skkdic-work*")))
352 ;; Setup and generate the header part of working buffer.
355 (buffer-disable-undo)
356 (insert ";; skkdic.el -- dictionary for Japanese input method\n"
357 ";;\tGenerated by the command `skkdic-convert'\n"
358 ";;\tDate: " (current-time-string) "\n"
359 ";;\tOriginal SKK dictionary file: "
360 (file-name-nondirectory filename)
363 ";; Do byte-compile this file again after any modification.\n\n"
364 ";;; Start of the header of the original SKK dictionary.\n\n")
369 (search-forward ";; okuri-ari")
373 (insert-buffer-substring skkbuf 1 pos))
377 ;; Generate the body part of working buffer.
381 ;; Convert okuri-ari entries.
382 (search-forward ";; okuri-nasi")
385 (narrow-to-region from to)
386 (skkdic-convert-okuri-ari skkbuf buf)
389 ;; Convert okuri-nasi postfix entries.
393 (re-search-forward "^\\cH")
394 (setq to (match-beginning 0))
395 (narrow-to-region from to)
396 (skkdic-convert-postfix skkbuf buf)
399 ;; Convert okuri-nasi prefix entries.
401 (skkdic-convert-prefix skkbuf buf)
404 (skkdic-collect-okuri-nasi)
406 ;; Convert okuri-nasi general entries.
407 (skkdic-convert-okuri-nasi skkbuf buf)
412 (goto-char (point-max))
413 (insert ";;\n(provide 'skkdic)\n\n;; skkdic.el ends here\n")))
415 ;; Save the working buffer.
417 (set-visited-file-name (expand-file-name skkdic-filename dirname) t)
418 (set-buffer-file-coding-system 'iso-2022-7bit-short)
421 (switch-to-buffer buf)))
423 (defun batch-skkdic-convert ()
424 "Run `skkdic-convert' on the files remaining on the command line.
425 Use this from the command line, with `-batch';
426 it won't work in an interactive Emacs.
428 % emacs -batch -l skkconv -f batch-skkdic-convert SKK-JISYO.L
429 to generate \"skkdic.el\" from SKK dictionary file \"SKK-JISYO.L\".
430 To get complete usage, invoke:
431 % emacs -batch -l skkconv -f batch-skkdic-convert -h"
432 (defvar command-line-args-left) ; Avoid compiler warning.
433 (if (not noninteractive)
434 (error "`batch-skkdic-convert' should be used only with -batch"))
435 (if (string= (car command-line-args-left) "-h")
437 (message "To convert SKK-JISYO.L into skkdic.el:")
438 (message " %% emacs -batch -l skkdic-conv -f batch-skkdic-convert SKK-JISYO.L")
439 (message "To convert SKK-JISYO.L into DIR/skkdic.el:")
440 (message " %% emacs -batch -l skkdic-conv -f batch-skkdic-convert -dir DIR SKK-JISYO.L"))
441 (let (targetdir filename)
442 (if (string= (car command-line-args-left) "-dir")
444 (setq command-line-args-left (cdr command-line-args-left))
445 (setq targetdir (expand-file-name (car command-line-args-left)))
446 (setq command-line-args-left (cdr command-line-args-left))))
447 (setq filename (expand-file-name (car command-line-args-left)))
448 (message "Converting %s to skkdic.el ..." filename)
449 (message "It takes around 10 minutes even on Sun SS20.")
450 (skkdic-convert filename targetdir)
451 (message "Do byte-compile the created file by:")
452 (message " %% emacs -batch -l skkdic-cnv -f batch-byte-compile skkdic.el")
453 (message " ^^^^^^^^^^^^^ -- Don't forget this option!")
458 ;; The following macros are expanded at byte-compiling time so that
459 ;; compiled code can be loaded quickly.
461 (defun skkdic-get-kana-compact-codes (kana)
462 (let* ((len (length kana))
463 (vec (make-vector len 0))
467 (setq ch (aref kana i))
469 (if (< ch 128) ; CH is an ASCII letter for OKURIGANA,
470 (- ch) ; represented by a negative code.
471 (if (= ch ?
\e$B!<
\e(B) ; `
\e$B!<
\e(B' is represented by 0.
473 (- (nth 2 (split-char ch)) 32))))
477 (defun skkdic-extract-conversion-data (entry)
478 (string-match "^\\cj+[a-z]* " entry)
479 (let ((kana (substring entry (match-beginning 0) (1- (match-end 0))))
482 (while (string-match "[^ ]+" entry i)
483 (setq candidates (cons (match-string 0 entry) candidates))
484 (setq i (match-end 0)))
485 (cons (skkdic-get-kana-compact-codes kana) candidates)))
487 (defmacro skkdic-set-okuri-ari (&rest entries)
488 `(defconst skkdic-okuri-ari
490 (map '(skkdic-okuri-ari))
493 (setq entry (skkdic-extract-conversion-data (car l)))
494 (set-nested-alist (car entry) (cdr entry) map)
498 (defmacro skkdic-set-postfix (&rest entries)
499 `(defconst skkdic-postfix
505 (setq entry (skkdic-extract-conversion-data (car l)))
506 (setq len (length (car entry)))
509 (let ((entry2 (lookup-nested-alist (car entry) map nil nil t)))
510 (if (consp (car entry2))
511 (let ((conversions (cdr entry)))
513 (if (not (member (car conversions) (car entry2)))
514 (setcar entry2 (cons (car conversions) (car entry2))))
515 (setq conversions (cdr conversions))))
516 (set-nested-alist (car entry) (cdr entry) map)))
521 (defmacro skkdic-set-prefix (&rest entries)
522 `(defconst skkdic-prefix
528 (setq entry (skkdic-extract-conversion-data (car l)))
529 (setq len (length (car entry)))
532 (let ((entry2 (lookup-nested-alist (car entry) map len nil t)))
533 (if (consp (car entry2))
534 (let ((conversions (cdr entry)))
536 (if (not (member (car conversions) (car entry2)))
537 (setcar entry2 (cons (car conversions) (car entry2))))
538 (setq conversions (cdr conversions))))
539 (set-nested-alist (car entry) (cdr entry) map len)))
544 (defmacro skkdic-set-okuri-nasi (&rest entries)
545 `(defconst skkdic-okuri-nasi
547 (map '(skdic-okuri-nasi))
551 (setq count (1+ count))
552 (if (= (% count 10) 0)
553 (message (format "%d entries" count)))
554 (setq entry (skkdic-extract-conversion-data (car l)))
555 (set-nested-alist (car entry) (cdr entry) map)
559 ;; skkdic-cnv.el ends here