Trailing whitepace deleted.
[bpt/emacs.git] / lisp / language / lao-util.el
index cb78b7f..7105ae1 100644 (file)
@@ -1,7 +1,8 @@
-;;; lao-util.el --- utilities for Lao
+;;; lao-util.el --- utilities for Lao -*- coding: iso-2022-7bit; -*-
 
 ;; Copyright (C) 1997 Electrotechnical Laboratory, JAPAN.
 ;; Licensed to the Free Software Foundation.
+;; Copyright (C) 2001 Free Software Foundation, Inc.
 
 ;; Keywords: multilingual, Lao
 
 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 ;; Boston, MA 02111-1307, USA.
 
+;;; Commentary:
+
 ;;; Code:
 
-;;;###autoload
-(defun setup-lao-environment ()
-  "Setup multilingual environment (MULE) for Lao."
-  (interactive)
-  (set-language-environment "Lao"))
+;; Setting information of Thai characters.
+
+(defconst lao-category-table (make-category-table))
+(define-category ?c "Lao consonant" lao-category-table)
+(define-category ?s "Lao semi-vowel" lao-category-table)
+(define-category ?v "Lao upper/lower vowel" lao-category-table)
+(define-category ?t "Lao tone" lao-category-table)
 
 (let ((l '((?\e(1!\e(B consonant "LETTER KOR  KAI'" "CHICKEN")
           (?\e(1"\e(B consonant "LETTER KHOR KHAI'" "EGG")
@@ -81,7 +86,7 @@
           (?\e(1Q\e(B vowel-upper "VOWEL SIGN MAI KAN")
           (?\e(1R\e(B vowel-base "VOWEL SIGN SARA AR")
           (?\e(1S\e(B vowel-base "VOWEL SIGN SARA AM")
-          (?\e(1T\e(B vowel-upper "VOWEL SIGN SARA I") 
+          (?\e(1T\e(B vowel-upper "VOWEL SIGN SARA I")
           (?\e(1U\e(B vowel-upper "VOWEL SIGN SARA II")
           (?\e(1V\e(B vowel-upper "VOWEL SIGN SARA EU")
           (?\e(1W\e(B vowel-upper "VOWEL SIGN SARA UR")
           (?\e(1c\e(B vowel-base "VOWEL SIGN SARA EI MAI MUAN\"")
           (?\e(1d\e(B vowel-base "VOWEL SIGN SARA AI MAI MAY")
           (?\e(1e\e(B invalid nil)
-          (?\e(1f\e(B special "KO LA (REPETITION)") 
+          (?\e(1f\e(B special "KO LA (REPETITION)")
           (?\e(1g\e(B invalid nil)
           (?\e(1h\e(B tone "TONE MAI EK")
           (?\e(1i\e(B tone "TONE MAI THO")
           (?\e(1|\e(B consonant "LETTER NHOR NHUU" "MOUSE")
           (?\e(1}\e(B consonant "LETTER MHOR MHAR" "DOG")
           (?\e(1~\e(B invalid nil)
-          ))
+          ;; Unicode equivalents
+          (?\e$,1D!\e(B consonant "LETTER KOR  KAI'" "CHICKEN")
+          (?\e$,1D"\e(B consonant "LETTER KHOR KHAI'" "EGG")
+          (?\e$,1D$\e(B consonant "LETTER QHOR QHWARGN" "BUFFALO")
+          (?\e$,1D'\e(B consonant "LETTER NGOR NGUU" "SNAKE")
+          (?\e$,1D(\e(B consonant "LETTER JOR JUA" "BUDDHIST NOVICE")
+          (?\e$,1D*\e(B consonant "LETTER XOR X\"ARNG" "ELEPHANT")
+          (?\e$,1D-\e(B consonant "LETTER YOR YUNG" "MOSQUITO")
+          (?\e$,1D4\e(B consonant "LETTER DOR DANG" "NOSE")
+          (?\e$,1D5\e(B consonant "LETTER TOR TAR" "EYE")
+          (?\e$,1D6\e(B consonant "LETTER THOR THUNG" "TO ASK,QUESTION")
+          (?\e$,1D7\e(B consonant "LETTER DHOR DHARM" "FLAG")
+          (?\e$,1D9\e(B consonant "LETTER NOR NOK" "BIRD")
+          (?\e$,1D:\e(B consonant "LETTER BOR BED" "FISHHOOK")
+          (?\e$,1D;\e(B consonant "LETTER POR PAR" "FISH")
+          (?\e$,1D<\e(B consonant "LETTER HPOR HPER\"" "BEE")
+          (?\e$,1D=\e(B consonant "LETTER FHOR FHAR" "WALL")
+          (?\e$,1D>\e(B consonant "LETTER PHOR PHUU" "MOUNTAIN")
+          (?\e$,1D?\e(B consonant "LETTER FOR FAI" "FIRE")
+          (?\e$,1DA\e(B consonant "LETTER MOR MAR\"" "HORSE")
+          (?\e$,1DB\e(B consonant "LETTER GNOR GNAR" "MEDICINE")
+          (?\e$,1DC\e(B consonant "LETTER ROR ROD" "CAR")
+          (?\e$,1DE\e(B consonant "LETTER LOR LIING" "MONKEY")
+          (?\e$,1DG\e(B consonant "LETTER WOR WII" "HAND FAN")
+          (?\e$,1DJ\e(B consonant "LETTER SOR SEA" "TIGER")
+          (?\e$,1DK\e(B consonant "LETTER HHOR HHAI" "JAR")
+          (?\e$,1DM\e(B consonant "LETTER OR OOW" "TAKE")
+          (?\e$,1DN\e(B consonant "LETTER HOR HEA" "BOAT")
+          (?\e$,1DO\e(B special "ELLIPSIS")
+          (?\e$,1DP\e(B vowel-base "VOWEL SIGN SARA A")
+          (?\e$,1DQ\e(B vowel-upper "VOWEL SIGN MAI KAN")
+          (?\e$,1DR\e(B vowel-base "VOWEL SIGN SARA AR")
+          (?\e$,1DS\e(B vowel-base "VOWEL SIGN SARA AM")
+          (?\e$,1DT\e(B vowel-upper "VOWEL SIGN SARA I")
+          (?\e$,1DU\e(B vowel-upper "VOWEL SIGN SARA II")
+          (?\e$,1DV\e(B vowel-upper "VOWEL SIGN SARA EU")
+          (?\e$,1DW\e(B vowel-upper "VOWEL SIGN SARA UR")
+          (?\e$,1DX\e(B vowel-lower "VOWEL SIGN SARA U")
+          (?\e$,1DY\e(B vowel-lower "VOWEL SIGN SARA UU")
+          (?\e$,1D[\e(B vowel-upper "VOWEL SIGN MAI KONG")
+          (?\e$,1D\\e(B semivowel-lower "SEMIVOWEL SIGN LO")
+          (?\e$,1D]\e(B vowel-base "SEMIVOWEL SIGN SARA IA")
+          (?\e$,1D`\e(B vowel-base "VOWEL SIGN SARA EE")
+          (?\e$,1Da\e(B vowel-base "VOWEL SIGN SARA AA")
+          (?\e$,1Db\e(B vowel-base "VOWEL SIGN SARA OO")
+          (?\e$,1Dc\e(B vowel-base "VOWEL SIGN SARA EI MAI MUAN\"")
+          (?\e$,1Dd\e(B vowel-base "VOWEL SIGN SARA AI MAI MAY")
+          (?\e$,1Df\e(B special "KO LA (REPETITION)")
+          (?\e$,1Dh\e(B tone "TONE MAI EK")
+          (?\e$,1Di\e(B tone "TONE MAI THO")
+          (?\e$,1Dj\e(B tone "TONE MAI TI")
+          (?\e$,1Dk\e(B tone "TONE MAI JADTAWAR")
+          (?\e$,1Dl\e(B tone "CANCELLATION MARK")
+          (?\e$,1Dm\e(B vowel-upper "VOWEL SIGN SARA OR")
+          (?\e$,1Dp\e(B special "DIGIT ZERO")
+          (?\e$,1Dq\e(B special "DIGIT ONE")
+          (?\e$,1Dr\e(B special "DIGIT TWO")
+          (?\e$,1Ds\e(B special "DIGIT THREE")
+          (?\e$,1Dt\e(B special "DIGIT FOUR")
+          (?\e$,1Du\e(B special "DIGIT FIVE")
+          (?\e$,1Dv\e(B special "DIGIT SIX")
+          (?\e$,1Dw\e(B special "DIGIT SEVEN")
+          (?\e$,1Dx\e(B special "DIGIT EIGHT")
+          (?\e$,1Dy\e(B special "DIGIT NINE")
+          (?\e$,1D|\e(B consonant "LETTER NHOR NHUU" "MOUSE")
+          (?\e$,1D}\e(B consonant "LETTER MHOR MHAR" "DOG")))
       elm)
   (while l
-    (setq elm (car l))
-    (put-char-code-property (car elm) 'phonetic-type (car (cdr elm)))
-    (put-char-code-property (car elm) 'name (nth 2 elm))
-    (put-char-code-property (car elm) 'meaning (nth 3 elm))
-    (setq l (cdr l))))
+    (setq elm (car l) l (cdr l))
+    (let ((char (car elm))
+         (ptype (nth 1 elm)))
+      (cond ((eq ptype 'consonant)
+            (modify-category-entry char ?c lao-category-table))
+           ((memq ptype '(vowel-upper vowel-lower))
+            (modify-category-entry char ?v lao-category-table))
+           ((eq ptype 'semivowel-lower)
+            (modify-category-entry char ?s lao-category-table))
+           ((eq ptype 'tone)
+            (modify-category-entry char ?t lao-category-table)))
+      (put-char-code-property char 'phonetic-type ptype)
+      (put-char-code-property char 'name (nth 2 elm))
+      (put-char-code-property char 'meaning (nth 3 elm)))))
+
+;; The general composing rules are as follows:
+;;
+;;                          T
+;;       V        T         V                  T
+;; CV -> C, CT -> C, CVT -> C, Cv -> C, CvT -> C
+;;                                   v         v
+;;                             T
+;;        V         T          V                   T
+;; CsV -> C, CsT -> C, CsVT -> C, Csv -> C, CvT -> C
+;;        s         s          s         s         s
+;;                                       v         v
+
+
+;; where C: consonant, V: vowel upper, v: vowel lower,
+;;       T: tone mark, s: semivowel lower
+
+(defvar lao-composition-pattern
+  "\\cc\\(\\ct\\|\\cv\\ct?\\|\\cs\\(\\ct\\|\\cv\\ct?\\)?\\)"
+  "Regular expression matching a Lao composite sequence.")
+
+;;;###autoload
+(defun lao-compose-string (str)
+  (with-category-table lao-category-table
+   (let ((idx 0))
+     (while (setq idx (string-match lao-composition-pattern str idx))
+       (compose-string str idx (match-end 0))
+       (setq idx (match-end 0))))
+   str))
+
+;;; LRT: Lao <-> Roman Transcription
+
+;; Upper vowels and tone-marks are put on the letter.
+;; Semi-vowel-sign-lo and lower vowels are put under the letter.
+
+(defconst lao-transcription-consonant-alist
+  (sort '(;; single consonants
+         ("k" . "\e(1!\e(B")
+         ("kh" . "\e(1"\e(B")
+         ("qh" . "\e(1$\e(B")
+         ("ng" . "\e(1'\e(B")
+         ("j" . "\e(1(\e(B")
+         ("s" . "\e(1J\e(B")
+         ("x" . "\e(1*\e(B")
+         ("y" . "\e(1-\e(B")
+         ("d" . "\e(14\e(B")
+         ("t" . "\e(15\e(B")
+         ("th" . "\e(16\e(B")
+         ("dh" . "\e(17\e(B")
+         ("n" . "\e(19\e(B")
+         ("b" . "\e(1:\e(B")
+         ("p" . "\e(1;\e(B")
+         ("hp" . "\e(1<\e(B")
+         ("fh" . "\e(1=\e(B")
+         ("ph" . "\e(1>\e(B")
+         ("f" . "\e(1?\e(B")
+         ("m" . "\e(1A\e(B")
+         ("gn" . "\e(1B\e(B")
+         ("l" . "\e(1E\e(B")
+         ("r" . "\e(1C\e(B")
+         ("v" . "\e(1G\e(B")
+         ("w" . "\e(1G\e(B")
+         ("hh" . "\e(1K\e(B")
+         ("O" . "\e(1M\e(B")
+         ("h" . "\e(1N\e(B")
+         ("nh" . "\e(1|\e(B")
+         ("mh" . "\e(1}\e(B")
+         ("lh" . ["\e(1K\\e(B"])
+         ;; double consonants
+         ("ngh" . ["\e(1K'\e(B"])
+         ("yh" . ["\e(1K]\e(B"])
+         ("wh" . ["\e(1KG\e(B"])
+         ("hl" . ["\e(1KE\e(B"])
+         ("hy" . ["\e(1K-\e(B"])
+         ("hn" . ["\e(1K9\e(B"])
+         ("hm" . ["\e(1KA\e(B"])
+         )
+       (function (lambda (x y) (> (length (car x)) (length (car y)))))))
+
+(defconst lao-transcription-semi-vowel-alist
+  '(("r" . "\e(1\\e(B")))
+
+(defconst lao-transcription-vowel-alist
+  (sort '(("a" . "\e(1P\e(B")
+         ("ar" . "\e(1R\e(B")
+         ("i" . "\e(1T\e(B")
+         ("ii" . "\e(1U\e(B")
+         ("eu" . "\e(1V\e(B")
+         ("ur" . "\e(1W\e(B")
+         ("u" . "\e(1X\e(B")
+         ("uu" . "\e(1Y\e(B")
+         ("e" . ["\e(1`P\e(B"])
+         ("ee" . "\e(1`\e(B")
+         ("ae" . ["\e(1aP\e(B"])
+         ("aa" . "\e(1a\e(B")
+         ("o" . ["\e(1bP\e(B"])
+         ("oo" . "\e(1b\e(B")
+         ("oe" . ["\e(1`RP\e(B"])
+         ("or" . "\e(1m\e(B")
+         ("er" . ["\e(1`T\e(B"])
+         ("ir" . ["\e(1`U\e(B"])
+         ("ua" . ["\e(1[GP\e(B"])
+         ("uaa" . ["\e(1[G\e(B"])
+         ("ie" . ["\e(1`Q]P\e(B"])
+         ("ia" . ["\e(1`Q]\e(B"])
+         ("ea" . ["\e(1`VM\e(B"])
+         ("eaa" . ["\e(1`WM\e(B"])
+         ("ai" . "\e(1d\e(B")
+         ("ei" . "\e(1c\e(B")
+         ("ao" . ["\e(1`[R\e(B"])
+         ("aM" . "\e(1S\e(B"))
+       (function (lambda (x y) (> (length (car x)) (length (car y)))))))
+
+;; Maa-sakod is put at the tail.
+(defconst lao-transcription-maa-sakod-alist
+  '(("k" . "\e(1!\e(B")
+    ("g" . "\e(1'\e(B")
+    ("y" . "\e(1-\e(B")
+    ("d" . "\e(14\e(B")
+    ("n" . "\e(19\e(B")
+    ("b" . "\e(1:\e(B")
+    ("m" . "\e(1A\e(B")
+    ("v" . "\e(1G\e(B")
+    ("w" . "\e(1G\e(B")
+    ))
+
+(defconst lao-transcription-tone-alist
+  '(("'" . "\e(1h\e(B")
+    ("\"" . "\e(1i\e(B")
+    ("^" . "\e(1j\e(B")
+    ("+" . "\e(1k\e(B")
+    ("~" . "\e(1l\e(B")))
+
+(defconst lao-transcription-punctuation-alist
+  '(("\\0" . "\e(1p\e(B")
+    ("\\1" . "\e(1q\e(B")
+    ("\\2" . "\e(1r\e(B")
+    ("\\3" . "\e(1s\e(B")
+    ("\\4" . "\e(1t\e(B")
+    ("\\5" . "\e(1u\e(B")
+    ("\\6" . "\e(1v\e(B")
+    ("\\7" . "\e(1w\e(B")
+    ("\\8" . "\e(1x\e(B")
+    ("\\9" . "\e(1y\e(B")
+    ("\\\\" . "\e(1f\e(B")
+    ("\\$" . "\e(1O\e(B")))
+
+(defconst lao-transcription-pattern
+  (concat
+   "\\("
+   (mapconcat 'car lao-transcription-consonant-alist "\\|")
+   "\\)\\("
+   (mapconcat 'car lao-transcription-semi-vowel-alist "\\|")
+   "\\)?\\(\\("
+   (mapconcat 'car lao-transcription-vowel-alist "\\|")
+   "\\)\\("
+   (mapconcat 'car lao-transcription-maa-sakod-alist "\\|")
+   "\\)?\\("
+   (mapconcat (lambda (x) (regexp-quote (car x)))
+             lao-transcription-tone-alist "\\|")
+   "\\)?\\)?\\|"
+   (mapconcat (lambda (x) (regexp-quote (car x)))
+             lao-transcription-punctuation-alist "\\|")
+   )
+  "Regexp of Roman transcription pattern for one Lao syllable.")
+
+(defconst lao-transcription-pattern
+  (concat
+   "\\("
+   (regexp-opt (mapcar 'car lao-transcription-consonant-alist))
+   "\\)\\("
+   (regexp-opt (mapcar 'car lao-transcription-semi-vowel-alist))
+   "\\)?\\(\\("
+   (regexp-opt (mapcar 'car lao-transcription-vowel-alist))
+   "\\)\\("
+   (regexp-opt (mapcar 'car lao-transcription-maa-sakod-alist))
+   "\\)?\\("
+   (regexp-opt (mapcar 'car lao-transcription-tone-alist))
+   "\\)?\\)?\\|"
+   (regexp-opt (mapcar 'car lao-transcription-punctuation-alist))
+   )
+  "Regexp of Roman transcription pattern for one Lao syllable.")
+
+(defconst lao-vowel-reordering-rule
+  '(("\e(1P\e(B" (0 ?\e(1P\e(B) (0 ?\e(1Q\e(B))
+    ("\e(1R\e(B" (0 ?\e(1R\e(B))
+    ("\e(1T\e(B" (0 ?\e(1U\e(B))
+    ("\e(1U\e(B" (0 ?\e(1U\e(B))
+    ("\e(1V\e(B" (0 ?\e(1V\e(B))
+    ("\e(1W\e(B" (0 ?\e(1W\e(B))
+    ("\e(1X\e(B" (0 ?\e(1X\e(B))
+    ("\e(1Y\e(B" (0 ?\e(1Y\e(B))
+    ("\e(1`P\e(B" (?\e(1`\e(B 0 ?\e(1P\e(B) (?\e(1`\e(B 0 ?\e(1Q\e(B))
+    ("\e(1`\e(B" (?\e(1`\e(B 0))
+    ("\e(1aP\e(B" (?\e(1a\e(B 0 ?\e(1P\e(B) (?\e(1a\e(B 0 ?\e(1Q\e(B))
+    ("\e(1a\e(B" (?\e(1a\e(B 0))
+    ("\e(1bP\e(B" (?\e(1b\e(B 0 ?\e(1P\e(B) (0 ?\e(1[\e(B) (?\e(1-\e(B ?\e(1b\e(B 0 ?\e(1Q\e(B) (?\e(1G\e(B ?\e(1b\e(B 0 ?\e(1Q\e(B))
+    ("\e(1b\e(B" (?\e(1b\e(B 0))
+    ("\e(1`RP\e(B" (?\e(1`\e(B 0 ?\e(1R\e(B ?\e(1P\e(B) (0 ?\e(1Q\e(B ?\e(1M\e(B))
+    ("\e(1m\e(B" (0 ?\e(1m\e(B) (0 ?\e(1M\e(B))
+    ("\e(1`T\e(B" (?\e(1`\e(B 0 ?\e(1T\e(B))
+    ("\e(1`U\e(B" (?\e(1`\e(B 0 ?\e(1U\e(B))
+    ("\e(1[GP\e(B" (0 ?\e(1[\e(B ?\e(1G\e(B ?\e(1P\e(B) (0 ?\e(1Q\e(B ?\e(1G\e(B))
+    ("\e(1[G\e(B" (0 ?\e(1[\e(B ?\e(1G\e(B) (0 ?\e(1G\e(B))
+    ("\e(1`Q]P\e(B" (?\e(1`\e(B 0 ?\e(1Q\e(B ?\e(1]\e(B ?\e(1P\e(B) (0 ?\e(1Q\e(B ?\e(1]\e(B))
+    ("\e(1`Q]\e(B" (?\e(1`\e(B 0 ?\e(1Q\e(B ?\e(1]\e(B) (0 ?\e(1]\e(B))
+    ("\e(1`VM\e(B" (?\e(1`\e(B 0 ?\e(1V\e(B ?\e(1M\e(B))
+    ("\e(1`WM\e(B" (?\e(1`\e(B 0 ?\e(1W\e(B ?\e(1M\e(B))
+    ("\e(1d\e(B" (?\e(1d\e(B 0))
+    ("\e(1c\e(B" (?\e(1c\e(B 0))
+    ("\e(1`[R\e(B" (?\e(1`\e(B 0 ?\e(1[\e(B ?\e(1R\e(B))
+    ("\e(1S\e(B" (0 ?\e(1S\e(B))
+
+    ;; Unicode equivalents
+    ("\e$,1DP\e(B" (0 ?\e$,1DP\e(B) (0 ?\e$,1DQ\e(B))
+    ("\e$,1DR\e(B" (0 ?\e$,1DR\e(B))
+    ("\e$,1DT\e(B" (0 ?\e$,1DU\e(B))
+    ("\e$,1DU\e(B" (0 ?\e$,1DU\e(B))
+    ("\e$,1DV\e(B" (0 ?\e$,1DV\e(B))
+    ("\e$,1DW\e(B" (0 ?\e$,1DW\e(B))
+    ("\e$,1DX\e(B" (0 ?\e$,1DX\e(B))
+    ("\e$,1DY\e(B" (0 ?\e$,1DY\e(B))
+    ("\e$,1D`DP\e(B" (?\e$,1D`\e(B 0 ?\e$,1DP\e(B) (?\e$,1D`\e(B 0 ?\e$,1DQ\e(B))
+    ("\e$,1D`\e(B" (?\e$,1D`\e(B 0))
+    ("\e$,1DaDP\e(B" (?\e$,1Da\e(B 0 ?\e$,1DP\e(B) (?\e$,1Da\e(B 0 ?\e$,1DQ\e(B))
+    ("\e$,1Da\e(B" (?\e$,1Da\e(B 0))
+    ("\e$,1DbDP\e(B" (?\e$,1Db\e(B 0 ?\e$,1DP\e(B) (0 ?\e$,1D[\e(B) (?\e$,1D-\e(B ?\e$,1Db\e(B 0 ?\e$,1DQ\e(B) (?\e$,1DG\e(B ?\e$,1Db\e(B 0 ?\e$,1DQ\e(B))
+    ("\e$,1Db\e(B" (?\e$,1Db\e(B 0))
+    ("\e$,1D`DRDP\e(B" (?\e$,1D`\e(B 0 ?\e$,1DR\e(B ?\e$,1DP\e(B) (0 ?\e$,1DQ\e(B ?\e$,1DM\e(B))
+    ("\e$,1Dm\e(B" (0 ?\e$,1Dm\e(B) (0 ?\e$,1DM\e(B))
+    ("\e$,1D`DT\e(B" (?\e$,1D`\e(B 0 ?\e$,1DT\e(B))
+    ("\e$,1D`DU\e(B" (?\e$,1D`\e(B 0 ?\e$,1DU\e(B))
+    ("\e$,1D[DGDP\e(B" (0 ?\e$,1D[\e(B ?\e$,1DG\e(B ?\e$,1DP\e(B) (0 ?\e$,1DQ\e(B ?\e$,1DG\e(B))
+    ("\e$,1D[DG\e(B" (0 ?\e$,1D[\e(B ?\e$,1DG\e(B) (0 ?\e$,1DG\e(B))
+    ("\e$,1D`DQD]DP\e(B" (?\e$,1D`\e(B 0 ?\e$,1DQ\e(B ?\e$,1D]\e(B ?\e$,1DP\e(B) (0 ?\e$,1DQ\e(B ?\e$,1D]\e(B))
+    ("\e$,1D`DQD]\e(B" (?\e$,1D`\e(B 0 ?\e$,1DQ\e(B ?\e$,1D]\e(B) (0 ?\e$,1D]\e(B))
+    ("\e$,1D`DVDM\e(B" (?\e$,1D`\e(B 0 ?\e$,1DV\e(B ?\e$,1DM\e(B))
+    ("\e$,1D`DWDM\e(B" (?\e$,1D`\e(B 0 ?\e$,1DW\e(B ?\e$,1DM\e(B))
+    ("\e$,1Dd\e(B" (?\e$,1Dd\e(B 0))
+    ("\e$,1Dc\e(B" (?\e$,1Dc\e(B 0))
+    ("\e$,1D`D[DR\e(B" (?\e$,1D`\e(B 0 ?\e$,1D[\e(B ?\e$,1DR\e(B))
+    ("\e$,1DS\e(B" (0 ?\e$,1DS\e(B)))
+  "Alist of Lao vowel string vs the corresponding re-ordering rule.
+Each element has this form:
+       (VOWEL NO-MAA-SAKOD-RULE WITH-MAA-SAKOD-RULE (MAA-SAKOD-0 RULE-0) ...)
+
+VOWEL is a vowel string (e.g. \"\e(1`Q]P\e(B\").
+
+NO-MAA-SAKOD-RULE is a rule to re-order and modify VOWEL following a
+consonant.  It is a list vowel characters or 0.  The element 0
+indicate the place to embed a consonant.
+
+Optional WITH-MAA-SAKOD-RULE is a rule to re-order and modify VOWEL
+follwoing a consonant and preceding a maa-sakod character.  If it is
+nil, NO-MAA-SAKOD-RULE is used.  The maa-sakod character is alwasy
+appended at the tail.
+
+For instance, rule `(\"\e(1`WM\e(B\" (?\e(1`\e(B t ?\e(1W\e(B ?\e(1M\e(B))' tells that this vowel
+string following a consonant `\e(1!\e(B' should be re-ordered as \"\e(1`!WM\e(B\".
+
+Optional (MAA-SAKOD-n RULE-n) are rules specially applied to maa-sakod
+character MAA-SAKOD-n.")
+
+;;;###autoload
+(defun lao-transcribe-single-roman-syllable-to-lao (from to &optional str)
+  "Transcribe a Romanized Lao syllable in the region FROM and TO to Lao string.
+Only the first syllable is transcribed.
+The value has the form: (START END LAO-STRING), where
+START and END are the beggining and end positions of the Roman Lao syllable,
+LAO-STRING is the Lao character transcription of it.
+
+Optional 3rd arg STR, if non-nil, is a string to search for Roman Lao
+syllable.  In that case, FROM and TO are indexes to STR."
+  (if str
+      (if (setq from (string-match lao-transcription-pattern str from))
+         (progn
+           (if (>= from to)
+               (setq from nil)
+             (setq to (match-end 0)))))
+    (save-excursion
+      (goto-char from)
+      (if (setq to (re-search-forward lao-transcription-pattern to t))
+         (setq from (match-beginning 0))
+       (setq from nil))))
+  (if from
+      (let* ((consonant (match-string 1 str))
+            (semivowel (match-string 3 str))
+            (vowel (match-string 5 str))
+            (maa-sakod (match-string 8 str))
+            (tone (match-string 9 str))
+            lao-consonant lao-semivowel lao-vowel lao-maa-sakod lao-tone
+            clen cidx)
+       (setq to (match-end 0))
+       (if (not consonant)
+           (setq str (cdr (assoc (match-string 0 str)
+                                 lao-transcription-punctuation-alist)))
+         (setq lao-consonant
+               (cdr (assoc consonant lao-transcription-consonant-alist)))
+         (if (vectorp lao-consonant)
+             (setq lao-consonant (aref lao-consonant 0)))
+         (setq clen (length lao-consonant))
+         (if semivowel
+             ;; Include semivowel in STR.
+             (setq lao-semivowel
+                   (cdr (assoc semivowel lao-transcription-semi-vowel-alist))
+                   str (if (= clen 1)
+                           (concat lao-consonant lao-semivowel)
+                         (concat (substring lao-consonant 0 1) lao-semivowel
+                                 (substring lao-consonant 1))))
+           (setq str lao-consonant))
+         (if vowel
+             (let (rule)
+               (setq lao-vowel
+                     (cdr (assoc vowel lao-transcription-vowel-alist)))
+               (if (vectorp lao-vowel)
+                   (setq lao-vowel (aref lao-vowel 0)))
+               (setq rule (assoc lao-vowel lao-vowel-reordering-rule))
+               (if (null maa-sakod)
+                   (setq rule (nth 1 rule))
+                 (setq lao-maa-sakod
+                       (cdr (assoc maa-sakod lao-transcription-maa-sakod-alist))
+                       rule
+                       (or (cdr (assq (aref lao-maa-sakod 0) (nthcdr 2 rule)))
+                           (nth 2 rule)
+                           (nth 1 rule))))
+               (or rule
+                   (error "Lao vowel %S has no re-ordering rule" lao-vowel))
+               (setq lao-consonant str str "")
+               (while rule
+                 (if (= (car rule) 0)
+                     (setq str (concat str lao-consonant)
+                           cidx (length str))
+                   (setq str (concat str (list (car rule)))))
+                 (setq rule (cdr rule)))
+               (or cidx
+                   (error "Lao vowel %S has malformed re-ordering rule" vowel))
+               ;; Set CIDX to after upper or lower vowel if any.
+               (let ((len (length str)))
+                 (while (and (< cidx len)
+                             (memq (get-char-code-property (aref str cidx)
+                                                           'phonetic-type)
+                                   '(vowel-lower vowel-upper)))
+                   (setq cidx (1+ cidx))))
+               (if lao-maa-sakod
+                   (setq str (concat str lao-maa-sakod)))
+               (if tone
+                   (setq lao-tone
+                         (cdr (assoc tone lao-transcription-tone-alist))
+                         str (concat (substring str 0 cidx) lao-tone
+                                     (substring str cidx)))))))
+       (list from to (lao-compose-string str)))))
+
+;;;###autoload
+(defun lao-transcribe-roman-to-lao-string (str)
+  "Transcribe Romanized Lao string STR to Lao character string."
+  (let ((from 0)
+       (to (length str))
+       (lao-str "")
+       val)
+    (while (setq val (lao-transcribe-single-roman-syllable-to-lao from to str))
+      (let ((start (car val))
+           (end (nth 1 val))
+           (lao (nth 2 val)))
+       (if (> start from)
+           (setq lao-str (concat lao-str (substring str from start) lao))
+         (setq lao-str (concat lao-str lao)))
+       (setq from end)))
+    (if (< from to)
+       (concat lao-str (substring str from to))
+      lao-str)))
+
+;;;###autoload
+(defun lao-post-read-conversion (len)
+  (lao-compose-region (point) (+ (point) len))
+  len)
+
+;;;###autoload
+(defun lao-composition-function (from to pattern &optional string)
+  "Compose Lao text in the region FROM and TO.
+The text matches the regular expression PATTERN.
+Optional 4th argument STRING, if non-nil, is a string containing text
+to compose.
+
+The return value is number of composed characters."
+  (if (< (1+ from) to)
+      (progn
+       (if string
+           (compose-string string from to)
+         (compose-region from to))
+       (- to from))))
+
+;;;###autoload
+(defun lao-compose-region (from to)
+  (interactive "r")
+  (save-restriction
+    (narrow-to-region from to)
+    (goto-char (point-min))
+    (with-category-table lao-category-table
+      (while (re-search-forward lao-composition-pattern nil t)
+       (compose-region (match-beginning 0) (point))))))
 
 ;;
 (provide 'lao-util)