*** empty log message ***
[m17n/m17n-lib-js.git] / xex.js
1 // -* coding: utf-8; -*
2
3 var Xex = {};
4
5 Xex.Error = {
6   UnknownError: "unknown-error",
7   WrongArgument: "wrong-argument",
8   // Load time errors.
9   InvalidInteger: "invalid-integer",
10   TermTypeInvalid: "term-type-invalid",
11   FunctionConflict: "function-conflict",
12   VariableTypeConflict: "variable-type-conflict",
13   VariableRangeConflict: "variable-range-conflict",
14   VariableWrongRange: "variable-wrong-range",
15   VariableWrongValue: "variable-wrong-value",
16
17   UnknownFunction: "unknown-function",
18   MacroExpansionError: "macro-expansion-error",
19   NoVariableName: "no-variable-name",
20   NoFunctionName: "no-funcion-name",
21
22   // Run time errors.
23   ArithmeticError: "arithmetic-error",
24   WrongType: "wrong-type",
25   IndexOutOfRange: "index-out-of-range",
26   ValueOutOfRange: "value-out-of-range",
27   NoLoopToBreak: "no-loop-to-break",
28   UncaughtThrow: "uncaught-throw"
29 };
30
31 Xex.Variable = function (domain, name, desc, val, range)
32 {
33   this.domain = domain;
34   this.name = name;
35   this.desc = desc;
36   this.val = val;
37   this.range = range;
38 }
39
40 Xex.Variable.prototype.clone = function ()
41 {
42   return new Xex.Variable (this.domain, this.name, this.desc,
43                            this.val, this.range);
44 }
45     
46 Xex.Variable.prototype.Equals = function (obj)
47 {
48   return ((obj instanceof Xex.Variable)
49           && obj.name == this.name);
50 }
51
52 Xex.Variable.prototype.SetValue = function (term)
53 {
54   this.val = term;
55   return term;
56 }
57
58 Xex.Function = function (name, with_var, min_args, max_args)
59 {
60   this.name = name;
61   this.with_var = with_var;
62   this.min_args = min_args;
63   this.max_args = max_args;
64 };  
65
66 Xex.Subrountine = function (builtin, name, with_var, min_args, max_args)
67 {
68   this.name = name;
69   this.with_var = with_var;
70   this.min_args = min_args;
71   this.max_args = max_args;
72   this.builtin = builtin;
73 }
74
75 Xex.Subrountine.prototype.Call = function (domain, vari, args)
76 {
77   newargs = new Array ();
78   for (var i = 0; i < args.length; i++)
79     {
80       newargs[i] = args[i].Eval (domain);
81       if (domain.Thrown ())
82         return newargs[i];
83     }
84   return this.builtin (domain, vari, newargs)
85 }
86
87 Xex.SpecialForm = function (builtin, name, with_var, min_args, max_args)
88 {
89   this.name = name;
90   this.with_var = with_var;
91   this.min_args = min_args;
92   this.max_args = max_args;
93   this.builtin = builtin;
94 }
95
96 Xex.SpecialForm.prototype.Call = function (domain, vari, args)
97 {
98   return this.builtin (domain, vari, args)
99 }
100
101 Xex.Lambda = function (name, min_args, max_args, args, body)
102 {
103   this.name = name;
104   this.min_args = min_args;
105   this.max_args = max_args;
106   this.args = args;
107   this.body = body;
108 }
109
110 Xex.Lambda.prototype.Call = function (domain, vari, args)
111 {
112   var current = domain.bindings;
113   var result = Xex.Zero;
114   var limit = max_args >= 0 ? args.length : args.length - 1;
115   var i;
116   
117   try {
118     for (i = 0; i < limit; i++)
119       {
120         result = args[i].Eval (domain);
121         if (domain.Thrown ())
122           return result;
123         domain.Bind (this.args[i], result);
124       }
125     if (max_args < 0)
126       {
127         var list = new Array ();
128         for (i = 0; i < args[limit].length; i++)
129           {
130             result = args[limit].Eval (domain);
131             if (domain.Thrown ())
132               return result;
133             list[i] = result;
134           }
135         domain.Bind (this.args[limit], list);
136       }
137     try {
138       domain.Catch (Xex.CatchTag.Return);
139       for (var term in this.body)
140         {
141           result = term.Eval (domain);
142           if (domain.Thrown ())
143             return result;
144         }
145     } finally {
146       domain.Uncatch ();
147     }
148   } finally {
149     domain.UnboundTo (current);
150   }
151   return result;
152 }
153
154 Xex.Macro = function (name, min_args, max_args, args, body)
155 {
156   this.name = name;
157   this.min_args = min_args;
158   this.max_args = max_args;
159   this.args = args;
160   this.body = body;
161 }
162
163 Xex.Macro.prototype.Call = function (domain, vari, args)
164 {
165   var current = domain.bindings;
166   var result = Xex.Zero;
167   var i;
168
169   try {
170     for (i = 0; i < args.length; i++)
171       domain.Bind (this.args[i], args[i]);
172     try {
173       domain.Catch (Xex.CatchTag.Return);
174       for (var term in body)
175         {
176           result = term.Eval (domain);
177           if (domain.Thrown ())
178             break;
179         }
180     } finally {
181       domain.Uncatch ();
182     }
183   } finally {
184     domain.UnboundTo (current);
185   }
186   return result;
187 }
188
189 Xex.Bindings = function (vari)
190 {
191   this.vari = vari;
192   this.old_value = vari.val;
193 }
194
195 Xex.Bindings.prototype.UnboundTo = function (boundary)
196 {
197   for (var b = this; b != boundary; b = b.next)
198     b.vari.val = b.old_value;
199   return boundary;
200 }
201
202 Xex.Bind = function (bindings, vari, val)
203 {
204   var b = new Xex.Bindings (vari);
205   b.vari.val = val;
206   b.next = bindings;
207   return b;
208 }
209
210 Xex.CatchTag = {
211   Return: 0,
212   Break: 1
213 }
214
215 Xex.Domain = function (name, parent, context)
216 {
217   this.name = name;
218   this.context = context;
219   this.depth = 0;
220
221   if (name != 'basic' && ! parent)
222     parent = Xex.BasicDomain
223   this.parent = parent;
224   this.termtypes = {};
225   this.functions = {};
226   this.variables = {};
227   if (parent)
228     {
229       var elt;
230       for (elt in parent.termtypes)
231         this.termtypes[elt] = parent.termtypes[elt];
232       for (elt in parent.functions)
233         this.functions[elt] = parent.functions[elt];
234       for (elt in parent.variables)
235         this.variables[elt] = parent.variables[elt];
236     }
237
238   this.call_stack = new Array ();
239   this.bindings = null;
240   this.catch_stack = new Array ();
241   this.catch_count = 0;
242   this.caught = false;
243 };
244
245 Xex.Domain.prototype = {
246   CallStackCount: function () { return this.call_stack.length; },
247   CallStackPush: function (term) { this.call_stack.push (term); },
248   CallStackPop: function () { this.call_stack.pop (); },
249   Bind: function (vari, val)
250   {
251     this.bindings = Xex.Bind (this.bindings, vari, val);
252   },
253   UnboundTo: function (boundary)
254   {
255     if (this.bindings)
256       this.bindings = this.bindings.UnboundTo (boundary);
257   },
258   Catch: function (tag) { this.catch_stack.push (tag); this.catch_count++; },
259   Uncatch: function ()
260   {
261     this.catch_stack.pop ();
262     if (this.catch_count > this.catch_stack.length)
263       this.catch_count--;
264   },
265   Thrown: function ()
266   {
267     if (this.catch_count < this.catch_stack.length)
268       {
269         this.caught = (this.catch_count == this.catch_stack.length - 1);
270         return true;
271       }
272     this.caught = false;
273     return false;
274   },
275   ThrowReturn: function ()
276   {
277     for (var i = this.catch_stack.length - 1; i >= 0; i--)
278       {
279         this.catch_count--;
280         if (this.catch_stack[i] == Xex.CatchTag.Return)
281           break;
282       }
283   },
284   ThrowBreak: function ()
285   {
286     if (this.catch_stack[this.catch_stack.length - 1] != Xex.CatchTag.Break)
287       throw new Xex.ErrTerm (Xex.Error.NoLoopToBreak,
288                            "No surrounding loop to break");
289     this.catch_count--;
290   },
291   ThrowSymbol: function (tag)
292   {
293     var i = this.catch_count;
294     for (var j = this.catch_stack.length - 1; j >= 0; j--)
295       {
296         i--;
297         if (Xex.CatchTag.Matches (this.catch_stack[i], tag))
298           {
299             this.catch_count = i;
300             return;
301           }
302       }
303     throw new Xex.ErrTerm (Xex.Error.UncaughtThrow,
304                          "No corresponding catch: " + tag);
305   },
306   DefType: function (obj)
307   {
308     var type = obj.type;
309     if (this.termtypes[type])
310       throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
311                            "Already defined: " + type);
312     if (this.functions[type])
313       throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
314                            "Already defined as a funciton or a macro: "
315                            + type);
316     this.termtypes[type] = obj.Parser;
317   },
318   DefSubr: function (builtin, name, with_var, min_args, max_args)
319   {
320     this.functions[name] = new Xex.Subrountine (builtin, name, with_var,
321                                                 min_args, max_args);
322   },
323   DefSpecial: function (builtin, name, with_var, min_args, max_args)
324   {
325     this.functions[name] = new Xex.SpecialForm (builtin, name, with_var,
326                                                 min_args, max_args);
327   },
328   Defun: function (name, min_args, max_args, args, body)
329   {
330     this.functions[name] =  new Xex.Lambda (name, min_args, max_args,
331                                             args, body);
332   },
333   DefunByFunc: function (func) { this.functions[func.name] = func; },
334   Defmacro: function (name, min_args, max_args, args, body)
335   {
336     this.functions[name] = new Xex.Macro (name, min_args, max_args,
337                                           args, body);
338   },
339   DefAlias: function (alias, fname)
340   {
341     var func = this.functions[fname];
342
343     if (! func)
344       throw new Xex.ErrTerm (Xex.Error.UnknownFunction, fname);
345     if (this.termtypes[alias])
346       throw new Xex.ErrTerm (Xex.Error.FunctionConflict,
347                            "Already defined as a term type: " + alias);
348     if (this.functions[alias])
349       throw new Xex.ErrTerm (Xex.Error.FunctionConflict,
350                            "Already defined as a function: " + alias);
351     this.functions[alias] = func;
352   },
353   Defvar: function (name, desc, val, range)
354   {
355     var vari = new Xex.Variable (this, name, desc, val, range);
356     this.variables[name] = vari;
357     return vari;
358   },
359   GetFunc: function (name)
360   {
361     var func = this.functions[name];
362     if (! func)
363       throw new Xex.ErrTerm (Xex.Error.UnknownFunction,
364                              "Unknown function: " + name);
365     return func;
366   },
367   CopyFunc: function (domain, name)
368   {
369     var func = this.functions[name];
370     domain.DefunByFunc (func);
371     return true;
372   },
373   CopyFuncAll: function (domain)
374   {
375     for (var elt in this.functions)
376       domain.DefunByFunc (this.functions[elt]);
377   },
378   GetVarCreate: function (name)
379   {
380     var vari = this.variables[name];
381     if (! vari)
382       vari = this.variables[name] = new Xex.Variable (this, name, null,
383                                                       Xex.Zero, null);
384     return vari;
385   },
386   GetVar: function (name) { return this.variables[name]; },
387   SaveValues: function ()
388   {
389     values = {};
390     for (var elt in this.variables)
391       {
392         //if (! this.variables[elt].val)
393         //alert ('unknown value of ' + elt);
394         //else
395           values[elt] = this.variables[elt].val.Clone ();
396       }
397     return values;
398   },
399   RestoreValues: function (values)
400   {
401     var name;
402     for (name in values)
403       {
404         var vari = this.variables[name];
405         vari.val = values[name];
406       }
407   },
408
409   Trace: function () {}
410 };
411
412 Xex.Term = function (type) { this.type = type; }
413 Xex.Term.prototype = {
414   IsTrue: function () { return true; },
415   Eval: function (domain) { return this.Clone (); },
416   Clone: function (domain) { return this; },
417   Equals: function (obj)
418   {
419     return (this.type == obj.type
420             && this.val
421             && obj.val == this.val);
422   },
423   Matches: function (obj) { return this.Equals (obj); },
424   toString: function ()
425   {
426     if (this.val != undefined)
427       return '<' + this.type + '>' + this.val + '</' + this.type + '>';
428     return '<' + this.type + '/>';
429   },
430   Intval: function () { throw new Xex.ErrTerm (Xex.Error.WrongType,
431                                                "Not an integer"); },
432   Strval: function () { throw new Xex.ErrTerm (Xex.Error.WrongType,
433                                                "Not a string"); }
434 };
435
436 Node.prototype.firstElement = function ()
437 {
438   for (var n = this.firstChild; n; n = n.nextSibling)
439     if (n.nodeType == 1)
440       return n;
441   return null;
442 }
443
444 Node.prototype.nextElement = function ()
445 {
446   for (var n = this.nextSibling; n; n = n.nextSibling)
447     if (n.nodeType == 1)
448       return n;
449   return null;
450 };
451
452 (function () {
453   function parse_defvar (domain, node)
454   {
455     var name = node.attributes['vname'].nodeValue;
456     if (! name)
457       throw new Xex.ErrTerm (Xex.Error.NoVariableName, node, '');
458     var vari = domain.variables[name];
459     var desc, val, range;
460     if (vari)
461       {
462         desc = vari.description;
463         val = vari.val;
464         range = vari.range;
465       }
466     node = node.firstElement ();
467     if (node && node.nodeName == 'description')
468       {
469         desc = node.firstChild.nodeValue;
470         node = node.nextElement ();
471       }
472     if (node)
473       {
474         val = Xex.Term.Parse (domain, node);
475         node = node.nextElement ();
476         if (node && node.nodeName == 'possible-values')
477           for (node = node.firstElement (); node; node = node.nextElement ())
478             {
479               var pval;
480               if (node.nodeName == 'range')
481                 {
482                   if (! val.IsInt)
483                     throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
484                                            'Range not allowed for ' + name);
485                   pval = new Array ();
486                   for (var n = node.firstElement (); n; n = n.nextElement ())
487                     {
488                       var v = Xex.Term.Parse (domain, n);
489                       if (! v.IsInt)
490                         throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
491                                                'Invalid range value: ' + val);
492                       pval.push (v);
493                     }
494                   }
495               else
496                 {
497                   pval = Xex.Term.Parse (domain, node);
498                   if (val.type != pval.type)
499                     throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
500                                            'Invalid possible value: ' + pval);
501                 }
502               if (! range)
503                 range = new Array ();
504               range.push (pval);
505           }
506       }
507     if (! val)
508       val = Xex.Zero;
509     domain.Defvar (name, desc, val, range);
510     return name;
511   }
512
513   function parse_defun_head (domain, node)
514   {
515     var name = node.attributes['fname'].nodeValue;
516     if (! name)
517       throw new Xex.ErrTerm (Xex.Error.NoFunctionName, node, '');
518     var args = new Array ();
519     var nfixed = 0, noptional = 0, nrest = 0;
520
521     node = node.firstElement ();
522     if (node && node.nodeName == 'args')
523       {
524         var n;
525         for (n = n.firstElement (); n; n = n.nextElement ())
526           {
527             if (n.nodeName == 'fixed')
528               nfixed++;
529             else if (n.nodeName == 'optional')
530               noptional++;
531             else if (n.nodeName == 'rest')
532               nrest++;
533             else
534               throw new Xex.ErrTerm (Xex.Error.WrongType, n, n.nodeName);
535           }
536         if (nrest > 1)
537           throw new Xex.ErrTerm (Xex.Error.WrongType, n, 'Too many <rest>');
538         for (n = node.firstElement (); n; n = n.nextElement ())
539           args.push (domain.DefVar (n.attributes['vname'].nodeValue));
540       }
541     args.min_args = nfixed;
542     args.max_args = nrest == 0 ? nfixed + noptional : -1;
543
544     if (node.nodeName == 'defun')
545       domain.Defun (name, args, null);
546     else
547       domain.Defmacro (name, args, null);
548     return name;
549   }
550
551   function parse_defun_body (domain, node)
552   {
553     var name = node.attributes['fname'].nodeValue;
554     var func = domain.GetFunc (name);
555     var body;
556     for (node = node.firstElement (); node; node = node.nextElement ())
557       if (node.nodeName != 'description' && node.nodeName != 'args')
558         break;
559     body = Xex.Term.Parse (domain, node, null);
560     func.body = body;
561   }
562
563   Xex.Term.Parse = function (domain, node, stop)
564   {
565     if (arguments.length == 2)
566       {
567         var name = node.nodeName;
568         var parser = domain.termtypes[name];
569
570         if (parser)
571           return parser (domain, node);
572         if (name == 'defun' || name == 'defmacro')
573           {
574             name = parse_defun_head (domain, node);
575             parse_defun_body (domain, node);
576             return new Xex.StrTerm (name);
577           }
578         if (name == 'defvar')
579           {
580             name = parse_defvar (domain, node);
581             return new Xex.StrTerm (name);
582           }
583         return new Xex.Funcall.prototype.Parser (domain, node);
584       }
585     for (var n = node; n && n != stop; n = n.nextElement ())
586       if (n.nodeName == 'defun' || n.nodeName == 'defmacro')
587         parse_defun_head (domain, n);
588     var terms = null;
589     for (var n = node; n && n != stop; n = n.nextElement ())
590       {
591         if (n.nodeName == 'defun' || n.nodeName == 'defmacro')
592           parse_defun_body (domain, n);
593         else if (n.nodeName == 'defvar')
594           parse_defvar (domain, n);
595         else
596           {
597             if (! terms)
598               terms = new Array ();
599             terms.push (Xex.Term.Parse (domain, n));
600           }
601       }
602     return terms;
603   }
604 }) ();
605
606 Xex.Varref = function (vname)
607 {
608   this.val = vname;
609 };
610
611 (function () {
612   var proto = new Xex.Term ('varref');
613
614   proto.Clone = function () { return new Xex.Varref (this.val); }
615   proto.Eval = function (domain)
616   {
617     if (! this.vari || this.vari.domain != domain)
618       this.vari = domain.GetVarCreate (this.val);
619     return this.vari.val;
620   }
621
622   proto.Parser = function (domain, node)
623   {
624     return new Xex.Varref (node.attributes['vname'].nodeValue);
625   }
626
627   Xex.Varref.prototype = proto;
628 }) ();
629
630 var null_args = new Array ();
631   
632 Xex.Funcall = function (func, vari, args)
633 {
634   this.func = func;
635   this.vari = vari;
636   this.args = args || null_args;
637 };
638
639 (function () {
640   var proto = new Xex.Term ('funcall');
641
642   proto.Parser = function (domain, node)
643   {
644     var fname = node.nodeName;
645     var attr;
646
647     if (fname == 'funcall')
648       fname = node.attributes['fname'].nodeValue;
649     var func = domain.GetFunc (fname);
650     var vari;
651     attr = node.attributes['vname'];
652     vari = attr != undefined ? domain.GetVarCreate (attr.nodeValue) : false;
653     var args = Xex.Term.Parse (domain, node.firstElement (), null);
654     return new Xex.Funcall (func, vari, args);
655   }
656
657   proto.New = function (domain, fname, vname, args)
658   {
659     var func = domain.GetFunc (fname);
660     var vari = vname ? domain.GetVarCreate (vname) : null;
661     var funcall = new Xex.Funcall (func, vari, args);
662     if (func instanceof Xex.Macro)
663       funcall = funcall.Eval (domain);
664     return funcall;
665   }
666
667   proto.Eval = function (domain)
668   {
669     domain.Trace (this);
670     domain.depth++;
671     try {
672       var result = this.func.Call (domain, this.vari, this.args);
673     } finally {
674       domain.depth--;
675       domain.Trace (' => ' + result + "\n");
676     }
677     return result;
678   }
679
680   proto.Clone = function ()
681   {
682     return new Xex.Funcall (this.func, this.vari, this.args);
683   }
684
685   proto.Equals = function (obj)
686   {
687     return (obj.type == 'funcall'
688             && obj.func == this.func
689             && obj.vari.Equals (this.vari)
690             && obj.args.length == this.func.length);
691   }
692
693   proto.toString = function ()
694   {
695     var arglist = ''
696     var len = this.args.length;
697     if (len == 0)
698       return '<' + this.func.name + '/>';
699     for (var i = 0; i < len; i++)
700       arglist += this.args[i].toString ();
701     return '<' + this.func.name + '>' + arglist + '</' + this.func.name + '>';
702   }
703
704   Xex.Funcall.prototype = proto;
705 }) ();
706
707 Xex.ErrTerm = function (ename, message, stack)
708 {
709   this.ename = ename;
710   this.message = message;
711   this.stack = stack;
712 };
713
714 (function () {
715   var proto = new Xex.Term ('error');
716
717   proto.IsError = true;
718
719   proto.Parser = function (domain, node)
720   {
721     return new Xex.ErrTerm (node.attributes['ename'].nodeValue,
722                             node.innerText, false);
723   }
724
725   proto.CallStack = function () { return stack; }
726
727   proto.SetCallStack = function (value) { statck = value; }
728
729   proto.Clone = function ()
730   {
731     return new Xex.ErrTerm (ename, message, false);
732   }
733
734   proto.Equals = function (obj)
735   {
736     return (obj.IsError
737             && obj.ename == ename && obj.message == message
738             && (obj.stack ? (stack && stack.length == obj.stack.length)
739                 : ! stack));
740   }
741
742   proto.Matches = function (obj)
743   {
744     return (obj.IsError && obj.ename == ename);
745   }
746
747   proto.toString = function ()
748   {
749     return '<error ename="' + this.ename + '">' + this.message + '</error>';
750   }
751
752   Xex.ErrTerm.prototype = proto;
753 }) ();
754
755 Xex.IntTerm = function (num) { this.val = num; };
756 (function () {
757   var proto = new Xex.Term ('integer');
758   proto.IsInt = true;
759   proto.Intval = function () { return this.val; };
760   proto.IsTrue = function () { return this.val != 0; }
761   proto.Clone = function () { return new Xex.IntTerm (this.val); }
762   proto.Parser = function (domain, node)
763   {
764     var str = node.firstChild.nodeValue;
765
766     if (str.charAt (0) == '?' && str.length == 2)
767       return new Xex.IntTerm (str.charCodeAt (1));
768     return new Xex.IntTerm (parseInt (node.firstChild.nodeValue));
769   }
770   Xex.IntTerm.prototype = proto;
771 }) ();
772
773 Xex.StrTerm = function (str) { this.val = str; };
774 (function () {
775   var proto = new Xex.Term ('string');
776   proto.IsStr = true;
777   proto.Strval = function () { return this.val; };
778   proto.IsTrue = function () { return this.val.length > 0; }
779   proto.Clone = function () { return new Xex.StrTerm (this.val); }
780   proto.Parser = function (domain, node)
781   {
782     return new Xex.StrTerm (node.firstChild.nodeValue);
783   }
784   Xex.StrTerm.prototype = proto;
785 }) ();
786
787 Xex.SymTerm = function (str) { this.val = str; };
788 (function () {
789   var proto = new Xex.Term ('symbol');
790   proto.IsSymbol = true;
791   proto.IsTrue = function () { return this.val != 'nil'; }
792   proto.Clone = function () { return new Xex.SymTerm (this.val); }
793   proto.Parser = function (domain, node)
794   {
795     return new Xex.SymTerm (node.firstChild.nodeValue);
796   }
797   Xex.SymTerm.prototype = proto;
798 }) ();
799
800 Xex.LstTerm = function (list) { this.val = list; };
801 (function () {
802   var proto = new Xex.Term ('list');
803   proto.IsList = true;
804   proto.IsTrue = function () { return this.val.length > 0; }
805   proto.Clone = function () { return new Xex.LstTerm (this.val.slice (0)); }
806
807   proto.Equals = function (obj)
808   {
809     if (obj.type != 'list' || obj.val.length != this.val.length)
810       return false;
811     var i, len = this.val.length;
812     for (i = 0; i < len; i++)
813       if (! this.val[i].Equals (obj.val[i]))
814         return false;
815     return true;
816   }
817
818   proto.Parser = function (domain, node)
819   {
820     var list = Xex.Term.Parse (domain, node.firstElement (), null);
821     return new Xex.LstTerm (list);
822   }
823
824   proto.toString = function ()
825   {
826     var len = this.val.length;
827
828     if (len == 0)
829       return '<list/>';
830     var str = '<list>';
831     for (var i = 0; i < len; i++)
832       str += this.val[i].toString ();
833     return str + '</list>';
834   }
835   Xex.LstTerm.prototype = proto;
836 }) ();
837
838 (function () {
839   var basic = new Xex.Domain ('basic', null, null);
840
841   function Fset (domain, vari, args)
842   {
843     return vari.SetValue (args[0]);
844   }
845
846   function maybe_set_intvar (vari, n)
847   {
848     var term = new Xex.IntTerm (n);
849     if (vari != null)
850       vari.SetValue (term);
851     return term;
852   }
853
854   function Fset (domain, vari, args)
855   {
856     if (! vari)
857       throw new Xex.ErrTerm (Xex.Error.NoVariableName,
858                              'No variable name to set');
859     vari.SetValue (args[0]);
860     return args[0];
861   }
862
863   function maybe_set_intvar (vari, n)
864   {
865     var term = new IntTerm (n);
866     if (vari)
867       vari.SetValue (term);
868     return term;
869   }
870
871   function Fadd (domain, vari, args)
872   {
873     var n = vari ? vari.val.Intval () : 0;
874     var len = args.length;
875
876     for (var i = 0; i < len; i++)
877       n += args[i].Intval ();
878     return maybe_set_intvar (vari, n);
879   }
880
881   function Fmul (domain, vari, args)
882   {
883     var n = vari ? vari.val.Intval () : 1;
884     for (var i = 0; i < args.length; i++)
885       n *= arg.Intval ();
886     return maybe_set_intvar (vari, n);
887   }
888
889   function Fsub (domain, vari, args)
890   {
891     var n, i;
892
893     if (! vari)
894       {
895         n = args[0].Intval ();
896         i = 1;
897       }
898     else
899       {
900         n = vari.val.Intval ();
901         i = 0;
902       }
903     while (i < args.length)
904       n -= args[i++].Intval ();
905     return maybe_set_intvar (vari, n);
906   }
907
908   function Fdiv (domain, vari, args)
909   {
910     var n, i;
911
912     if (! vari == null)
913       {
914         n = args[0].Intval ();
915         i = 1;
916       }
917     else
918       {
919         n = vari.val.Intval ();
920         i = 0;
921       }
922     while (i < args.length)
923       n /= args[i++].Intval ();
924     return maybe_set_intvar (vari, n);
925   }
926
927   function Fmod (domain, vari, args)
928   {
929     return maybe_set_intvar (vari, args[0].Intval () % args[1].Intval ());
930   }
931
932   function Flogior (domain, vari, args)
933   {
934     var n = vari == null ? 0 : vari.val;
935     for (var i = 0; i < args.length; i++)
936       n |= args[i].val;
937     return maybe_set_intvar (vari, n);
938   }
939
940   function Fand (domain, vari, args)
941   {
942     var len = args.length;
943     for (var i = 0; i < len; i++)
944     {
945       var result = args[i].Eval (domain);
946       if (domain.Thrown ())
947         return result;
948       if (! result.IsTrue ())
949         return Xex.Zero;
950     }
951     return Xex.One;
952   }
953
954   function For (domain, vari, args)
955   {
956     var len = args.length;
957     for (var i = 0; i < len; i++)
958     {
959       var result = args[i].Eval (domain);
960       if (domain.Thrown ())
961         return result;
962       if (result.IsTrue ())
963         return Xex.One;
964     }
965     return Xex.Zero;
966   }
967
968   function Feq (domain, vari, args)
969   {
970     for (var i = 1; i < args.length; i++)
971       if (! args[i - 1].Equals (args[i]))
972         return Xex.Zero;
973     return Xex.One;
974   }
975
976   function Flt (domain, vari, args)
977   {
978     var n = args[0].Intval;
979
980     for (var i = 1; i < args.length; i++)
981       {
982         var n1 = args[i].Intval;
983         if (n >= n1)
984           return Xex.Zero;
985         n = n1;
986       }
987     return Xex.One;
988   }
989
990   function Fle (domain, vari, args)
991   {
992     var n = args[0].Intval;
993     for (var i = 1; i < args.length; i++)
994       {
995         var n1 = args[i].Intval;
996         if (n > n1)
997           return Xex.Zero;
998         n = n1;
999       }
1000     return Xex.One;
1001   }
1002
1003   function Fgt (domain, vari, args)
1004   {
1005     var n = args[0].Intval;
1006     for (var i = 1; i < args.length; i++)
1007       {
1008         var n1 = args[i].Intval;
1009         if (n <= n1)
1010           return Xex.Zero;
1011         n = n1;
1012       }
1013     return Xex.One;
1014   }
1015
1016   function Fge (domain, vari, args)
1017   {
1018     var n = args[0].Intval;
1019     for (var i = 1; i < args.Length; i++)
1020       {
1021         var n1 = args[i].Intval;
1022         if (n < n1)
1023           return Xex.Zero;
1024         n = n1;
1025       }
1026     return Xex.One;
1027   }
1028
1029   function Fprogn (domain, vari, args)
1030   {
1031     var result = Xex.One;
1032     var len = args.length;
1033
1034     for (var i = 0; i < len; i++)
1035       {
1036         result = args[i].Eval (domain);
1037         if (domain.Thrown ())
1038           return result;
1039       }
1040     return result;
1041   }
1042
1043   function Fif (domain, vari, args)
1044   {
1045     var result = args[0].Eval (domain);
1046
1047     if (domain.Thrown ())
1048       return result;
1049     if (result.IsTrue ())
1050       return args[1].Eval (domain);
1051     if (args.length == 2)
1052       return Zero;
1053     return args[2].Eval (domain);
1054   }
1055
1056   function Fcond (domain, vari, args)
1057   {
1058     for (var i = 0; i < args.length; i++)
1059       {
1060         var list = args[i].val;
1061         var result = list.val[0].Eval (doamin);
1062         if (result.isTrue ())
1063           {
1064             for (var j = 1; j < list.val.length; j++)
1065               {
1066                 domain.depth++;
1067                 result = list.val[j].Eval (domain);
1068                 domain.depth--;
1069                 if (domain.Thrown ())
1070                   return result;
1071                 }
1072             return result;
1073           }
1074       }
1075     return Xex.Zero;
1076   }
1077
1078   function eval_terms (domain, terms, idx)
1079   {
1080     var result = Xex.Zero;
1081     domain.caught = false;
1082     for (var i = idx; i < terms.length; i++)
1083       {
1084         result = terms[i].Eval (domain);
1085         if (domain.Thrown ())
1086           return result;
1087       }
1088     return result;
1089   }
1090
1091   function Fcatch (domain, vari, args)
1092   {
1093     var caught = false;
1094     var result;
1095
1096     if (args[0].IsError)
1097       {
1098         try {
1099           result = eval_terms (domain, args, 1);
1100         } catch (e) {
1101           if (e instanceof Xex.ErrTerm)
1102             {
1103               if (! args[0].Matches (e))
1104                 throw e;
1105               if (vari)
1106                 vari.SetValue (e);
1107               return Xex.One;
1108             }
1109         }
1110       }
1111     else if (args[0].IsSymbol)
1112       {
1113         try {
1114           domain.Catch (args[0].val);
1115           result = eval_terms (domain, args, 1);
1116           if (domain.caught)
1117             {
1118               if (vari != null)
1119                 vari.SetValue (result);
1120               return Xex.One;
1121             }
1122           return Xex.Zero;
1123         } finally {
1124           domain.Uncatch ();
1125         }
1126       }
1127     throw new Xex.ErrTerm (Xex.Error.WrongArgument,
1128                            "Not a symbol nor an error: " + args[0]);
1129   }
1130
1131   function Fthrow (domain, vari, args)
1132   {
1133     if (args[0].IsSymbl)
1134       {
1135         domain.ThrowSymbol (args[0]);
1136         return (args[args.length - 1]);
1137       }
1138     if (args[0].IsError)
1139       {
1140         throw args[0];
1141       }
1142     throw new Xex.ErrTerm (Xex.Error.WrongArgument,
1143                            "Not a symbol nor an error:" + args[0]);
1144   }
1145
1146   Xex.BasicDomain = basic;
1147
1148   basic.DefSubr (Fset, "set", true, 1, 1);
1149   if (basic.functions['='])
1150     alert (basic.functions['=']);
1151   basic.DefAlias ("=", "set");
1152   //basic.DefSubr (Fnot, "not", false, 1, 1);
1153   //basic.DefAlias ("!", "not");
1154   basic.DefSubr (Fadd, "add", true, 1, -1);
1155   basic.DefSubr (Fmul, "mul", true, 1, -1);
1156   basic.DefAlias ("*", "mul");
1157   basic.DefSubr (Fsub, "sub", true, 1, -1);
1158   basic.DefAlias ("-", "sub");
1159   basic.DefSubr (Fdiv, "div", true, 1, -1);
1160   basic.DefAlias ("/", "div");
1161   basic.DefSubr (Fmod, "mod", true, 1, 2);
1162   basic.DefAlias ("%", "mod");
1163   basic.DefSubr (Flogior, "logior", true, 1, -1);
1164   basic.DefAlias ('|', "logior");
1165   //basic.DefSubr (Flogand, "logand", true, 1, -1);
1166   //basic.DefAlias ("&", "logand");
1167   //basic.DefSubr (Flsh, "lsh", true, 1, 2);
1168   //basic.DefAlias ("<<", "lsh");
1169   //basic.DefSubr (Frsh, "rsh", true, 1, 2);
1170   //basic.DefAlias (">>", "rsh");
1171   basic.DefSubr (Feq, "eq", false, 2, -1);
1172   basic.DefAlias ("==", "eq");
1173   //basic.DefSubr (Fnoteq, "noteq", false, 2, 2);
1174   //basic.DefAlias ("!=", "noteq");
1175   basic.DefSubr (Flt, "lt", false, 2, -1);
1176   basic.DefAlias ("<", "lt");
1177   basic.DefSubr (Fle, "le", false, 2, -1);
1178   basic.DefAlias ("<=", "le");
1179   basic.DefSubr (Fgt, "gt", false, 2, -1);
1180   basic.DefAlias (">", "gt");
1181   basic.DefSubr (Fge, "ge", false, 2, -1);
1182   basic.DefAlias (">=", "ge");
1183   basic.DefSubr (Fthrow, "throw", false, 1, 2);
1184
1185   //basic.DefSubr (Fappend, "append", true, 0, -1);
1186   //basic.DefSubr (Fconcat, "concat", true, 0, -1);
1187   //basic.DefSubr (Fnth, "nth", false, 2, 2);
1188   //basic.DefSubr (Fcopy, "copy", false, 1, 1);
1189   //basic.DefSubr (Fins, "ins", true, 2, 2);
1190   //basic.DefSubr (Fdel, "del", true, 2, 2);
1191   //basic.DefSubr (Feval, "eval", false, 1, 1);
1192   //basic.DefSubr (Fbreak, "break", false, 0, 1);
1193   //basic.DefSubr (Freturn, "return", false, 0, 1);
1194   //basic.DefSubr (Fthrow, "throw", false, 1, 2);
1195
1196   basic.DefSpecial (Fand, "and", false, 1, -1);
1197   basic.DefAlias ("&&", "and");
1198   basic.DefSpecial (For, "or", false, 1, -1);
1199   basic.DefAlias ("||", "or");
1200   basic.DefSpecial (Fprogn, "progn", false, 1, -1);
1201   basic.DefAlias ("expr", "progn");
1202   basic.DefSpecial (Fif, "if", false, 2, 3);
1203   //basic.DefSpecial (Fwhen, "when", false, 1, -1);
1204   //basic.DefSpecial (Floop, "loop", false, 1, -1);
1205   //basic.DefSpecial (Fwhile, "while", false, 1, -1);
1206   basic.DefSpecial (Fcond, "cond", false, 1, -1);
1207   //basic.DefSpecial (Fforeach, "foreach", true, 2, -1);
1208   //basic.DefSpecial (Fquote, "quote", false, 1, 1);
1209   //basic.DefSpecial (Ftype, "type", false, 1, 1);
1210   basic.DefSpecial (Fcatch, "catch", true, 2, -1);
1211
1212   basic.DefType (Xex.Funcall.prototype);
1213   basic.DefType (Xex.Varref.prototype);
1214   basic.DefType (Xex.ErrTerm.prototype);
1215   basic.DefType (Xex.IntTerm.prototype);
1216   basic.DefType (Xex.StrTerm.prototype);
1217   basic.DefType (Xex.SymTerm.prototype);
1218   basic.DefType (Xex.LstTerm.prototype);
1219
1220 }) ();
1221
1222 Xex.Zero = new Xex.IntTerm (0);
1223 Xex.One = new Xex.IntTerm (1);
1224 Xex.nil = new Xex.SymTerm ('nil');
1225
1226 Xex.Load = function (server, file)
1227 {
1228   var obj = new XMLHttpRequest ();
1229   var url = server ? server + '/' + file : file;
1230   obj.open ('GET', url, false);
1231   obj.overrideMimeType ('text/xml');
1232   obj.send ('');
1233   return obj.responseXML.firstChild;
1234 }
1235
1236 var MIM = {
1237   // URL of the input method server.
1238   server: "http://www.m17n.org/common/mim-js",
1239   // Boolean flag to tell if MIM is active or not.
1240   enabled: true,
1241   // Boolean flag to tell if MIM is running in debug mode or not.
1242   debug: false,
1243   // List of main input methods.
1244   imlist: {},
1245   // List of extra input methods;
1246   imextra: {},
1247   // Global input method data
1248   im_global: null,
1249   // Currently selected input method.
1250   current: false,
1251
1252   // enum
1253   LoadStatus: { NotLoaded:0, Loading:1, Loaded:2, Error:-1 },
1254   ChangedStatus: {
1255     None:       0x00,
1256     StateTitle: 0x01,
1257     PreeditText:0x02,
1258     CursorPos:  0x04,
1259     CandidateList:0x08,
1260     CandidateIndex:0x10,
1261     CandidateShow:0x20,
1262     Preedit:    0x06,           // PreeditText | CursorPos
1263     Candidate:  0x38 // CandidateList | CandidateIndex | CandidateShow
1264   },
1265   KeyModifier: {
1266     SL: 0x00400000,
1267     SR: 0x00800000,
1268     S:  0x00C00000,
1269     CL: 0x01000000,
1270     CR: 0x02000000,
1271     C:  0x03000000,
1272     AL: 0x04000000,
1273     AR: 0x08000000,
1274     A:  0x0C000000,
1275     ML: 0x04000000,
1276     MR: 0x08000000,
1277     M:  0x0C000000,
1278     G:  0x10000000,
1279     s:  0x20000000,
1280     H:  0x40000000,
1281     High:       0x70000000,
1282     All:        0x7FC00000
1283   },
1284   Error: {
1285     ParseError: "parse-error"
1286   }
1287 };
1288   
1289 (function () {
1290   var keysyms = new Array ();
1291   keysyms["bs"] = "backspace";
1292   keysyms["lf"] = "linefeed";
1293   keysyms["cr"] = keysyms["enter"] = "return";
1294   keysyms["esc"] = "escape";
1295   keysyms["spc"] = "space";
1296   keysyms["del"] = "delete";
1297
1298   function decode_keysym (str) {
1299     var parts = str.split ("-");
1300     var len = parts.length, i;
1301     var has_modifier = len > 1;
1302
1303     for (i = 0; i < len - 1; i++)
1304       if (! MIM.KeyModifier.hasOwnProperty (parts[i]))
1305         return false;
1306     var key = parts[len - 1];
1307     if (key.length > 1)
1308       {
1309         key = keysyms[key.toLowerCase ()];
1310         if (key)
1311           {
1312             if (len > 1)
1313               {
1314                 str = parts[0];
1315                 for (i = 1; i < len - 1; i++)
1316                   str += '-' + parts[i];
1317                 str += '-' + key;
1318               }
1319             else
1320               str = key;
1321           }
1322       }
1323     if (has_modifier)
1324       {
1325         parts = new Array ();
1326         parts.push (str);
1327         return parts;
1328       }
1329     return str;
1330   }
1331
1332   MIM.Key = function (val)
1333   {
1334     this.key;
1335     this.has_modifier = false;
1336     if (typeof val == 'string' || val instanceof String)
1337       {
1338         this.key = decode_keysym (val);
1339         if (! this.key)
1340           throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + val);
1341         if (this.key instanceof Array)
1342           {
1343             this.key = this.key[0];
1344             this.has_modifier = true;
1345           }
1346       }
1347     else if (typeof val == 'number' || val instanceof Number)
1348       this.key = String.fromCharCode (val);
1349     else
1350       throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + val);
1351   }
1352
1353   MIM.Key.prototype.toString = function () { return this.key; };
1354 }) ();
1355
1356 (function () {
1357   MIM.KeySeq = function (seq)
1358   {
1359     this.val = new Array ();
1360     this.has_modifier = false;
1361
1362     if (seq)
1363       {
1364         if (seq.IsList)
1365           {
1366             var len = seq.val.length;
1367             for (var i = 0; i < len; i++)
1368               {
1369                 var v = seq.val[i];
1370                 if (v.type != 'string' && v.type != 'integer'
1371                     && v.type != 'symbol')
1372                   throw new Xex.ErrTerm (MIM.Error.ParseError,
1373                                          "Invalid key: " + v);
1374                 var key = new MIM.Key (v.val);
1375                 this.val.push (key);
1376                 if (key.has_modifier)
1377                   this.has_modifier = true;
1378               }
1379           }
1380         else if (seq.IsStr)
1381           {
1382             var len = seq.val.length;
1383             for (var i = 0; i < len; i++)
1384               this.val.push (new MIM.Key (seq.val.charCodeAt (i)));
1385           }
1386         else
1387           throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + seq);
1388       }
1389   }
1390
1391   var proto = new Xex.Term ('keyseq');
1392   proto.Clone = function () { return this; }
1393   proto.Parser = function (domain, node)
1394   {
1395     var seq = new Array ();
1396     for (node = node.firstChild; node; node = node.nextSibling)
1397       if (node.nodeType == 1)
1398         {
1399           var term = Xex.Term.Parse (domain, node);
1400           return new MIM.KeySeq (term);
1401         }
1402     throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid keyseq");
1403   }
1404   proto.toString = function ()
1405   {
1406     var len = this.val.length;
1407     if (len == 0)
1408       return '<keyseq/>';
1409     var first = true;
1410     var str = '<keyseq>';
1411     for (var i = 0; i < len; i++)
1412       {
1413         if (first)
1414           first = false;
1415         else if (this.has_modifier)
1416           str += ' ';
1417         str += this.val[i].toString ();
1418       }
1419     return str + '</keyseq>';
1420   }
1421
1422   MIM.KeySeq.prototype = proto;
1423 }) ();
1424
1425 (function () {
1426   MIM.Marker = function () { }
1427   MIM.Marker.prototype = new Xex.Term ('marker');
1428   MIM.Marker.prototype.CharAt = function (ic)
1429   {
1430     var p = this.Position (ic);
1431     if (p < 0)
1432       return ic.GetSurroundingChar (p);
1433     else if (p >= ic.preedit.length)
1434       return ic.GetSurroundingChar (p - ic.preedit.length);
1435     return ic.preedit.charCodeAt (p);
1436   }
1437
1438   MIM.NamedMarker = function (name) { this.val = name; }
1439   MIM.NamedMarker.prototype = new MIM.Marker ();
1440   MIM.NamedMarker.prototype.Position = function (ic)
1441   {
1442     var p = ic.marker_positions[this.val];
1443     return (p == undefined ? 0 : p);
1444   }
1445   MIM.NamedMarker.prototype.Mark = function (ic)
1446   {
1447     ic.marker_positions[this.val] = ic.cursor_pos;
1448   }
1449
1450   MIM.PredefinedMarker = function (name) { this.val = name; }
1451   MIM.PredefinedMarker.prototype = new MIM.Marker ();
1452   MIM.PredefinedMarker.prototype.Position = function (ic)
1453   {
1454     if (typeof this.pos == 'number')
1455       return this.pos;
1456     return this.pos (ic);
1457   }
1458
1459   var predefined = { }
1460
1461   function def_predefined (name, position)
1462   {
1463     predefined[name] = new MIM.PredefinedMarker (name);
1464     predefined[name].pos = position;
1465   }
1466
1467   def_predefined ('@<', 0);
1468   def_predefined ('@>', function (ic) { return ic.preedit.length; });
1469   def_predefined ('@-', function (ic) { return ic.cursor_pos - 1; });
1470   def_predefined ('@+', function (ic) { return ic.cursor_pos + 1; });
1471   def_predefined ('@[', function (ic) {
1472     if (ic.cursor_pos > 0)
1473       {
1474         var pos = ic.cursor_pos;
1475         return ic.preedit.FindProp ('candidates', pos - 1).from;
1476       }
1477     return 0;
1478   });
1479   def_predefined ('@]', function (ic) {
1480     if (ic.cursor_pos < ic.preedit.length - 1)
1481       {
1482         var pos = ic.cursor_pos;
1483         return ic.preedit.FindProp ('candidates', pos).to;
1484       }
1485     return ic.preedit.length;
1486   });
1487   for (var i = 0; i < 10; i++)
1488     def_predefined ("@" + i, i);
1489   predefined['@first'] = predefined['@<'];
1490   predefined['@last'] = predefined['@>'];
1491   predefined['@previous'] = predefined['@-'];
1492   predefined['@next'] = predefined['@+'];
1493   predefined['@previous-candidate-change'] = predefined['@['];
1494   predefined['@next-candidate-change'] = predefined['@]'];
1495
1496   MIM.SurroundMarker = function (name)
1497   {
1498     this.val = name;
1499     this.distance = parseInt (name.slice (2));
1500     if (isNaN (this.distance))
1501       throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid marker: " + name);
1502   }
1503   MIM.SurroundMarker.prototype = new MIM.Marker ();
1504   MIM.SurroundMarker.prototype.Position = function (ic)
1505   {
1506     return ic.cursor_pos + this.distance;
1507   }
1508
1509   MIM.Marker.prototype.Parser = function (domain, node)
1510   {
1511     var name = node.firstChild.nodeValue;
1512     if (name.charAt (0) == '@')
1513       {
1514         var n = predefined[name];
1515         if (n)
1516           return n;
1517         if (name.charAt (1) == '-')
1518           return new MIM.SurroundMarker (name);
1519         throw new Xex.ErrTerm (MIM.Error.ParseError,
1520                                "Invalid marker: " + name);
1521       }
1522     return new MIM.NamedMarker (name);
1523   }
1524 }) ();
1525
1526 MIM.Selector = function (name)
1527 {
1528   this.val = name;
1529 }
1530 MIM.Selector.prototype = new Xex.Term ('selector');
1531
1532 (function () {
1533   var selectors = {};
1534   selectors["@<"] = selectors["@first"] = new MIM.Selector ('@<');
1535   selectors["@="] = selectors["@current"] = new MIM.Selector ('@=');
1536   selectors["@>"] = selectors["@last"] = new MIM.Selector ('@>');
1537   selectors["@-"] = selectors["@previous"] = new MIM.Selector ('@-');
1538   selectors["@+"] = selectors["@next"] = new MIM.Selector ('@+');
1539   selectors["@["] = selectors["@previous-candidate-change"]
1540     = new MIM.Selector ('@[');
1541   selectors["@]"] = selectors["@next-candidate-change"]
1542     = new MIM.Selector ('@]');
1543
1544   MIM.Selector.prototype.Parser = function (domain, node)
1545   {
1546     var name = node.firstChild.nodeValue;
1547     var s = selectors[name];
1548     if (! s)
1549       throw new Xex.ErrTerm (MIM.Error.ParseError,
1550                              "Invalid selector: " + name);
1551     return s;
1552   }
1553 }) ();
1554
1555 MIM.Rule = function (keyseq, actions)
1556 {
1557   this.keyseq = keyseq;
1558   this.actions = actions;
1559 }
1560 MIM.Rule.prototype = new Xex.Term ('rule');
1561 MIM.Rule.prototype.Parser = function (domain, node)
1562 {
1563   var n;
1564   for (n = node.firstChild; n && n.nodeType != 1; n = n.nextSibling);
1565   if (! n)
1566     throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid rule:" + node);
1567   var keyseq = Xex.Term.Parse (domain, n);
1568   if (keyseq.type != 'keyseq')
1569     throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid rule:" + node);
1570   var actions = Xex.Term.Parse (domain, n.nextElement (), null);
1571   return new MIM.Rule (keyseq, actions);
1572 }
1573 MIM.Rule.prototype.toString = function ()
1574 {
1575   return '<rule/>';
1576 }
1577
1578 MIM.Map = function (name)
1579 {
1580   this.name = name;
1581   this.rules = new Array ();
1582 };
1583
1584 (function () {
1585   var proto = new Xex.Term ('map');
1586
1587   proto.Parser = function (domain, node)
1588   {
1589     var name = node.attributes['mname'].nodeValue;
1590     if (! name)
1591       throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid map");
1592     var map = new MIM.Map (name);
1593     for (var n = node.firstChild; n; n = n.nextSibling)
1594       if (n.nodeType == 1)
1595         map.rules.push (Xex.Term.Parse (domain, n));
1596     return map;
1597   }
1598
1599   proto.toString = function ()
1600   {
1601     var str = '<map mname="' + this.name + '">';
1602     var len = this.rules.length;
1603     for (i = 0; i < len; i++)
1604       str += this.rules[i];
1605     return str + '</map>';
1606   }
1607
1608   MIM.Map.prototype = proto;
1609 }) ();
1610
1611 Xex.CatchTag._mimtag = new Xex.SymTerm ('@mimtag');
1612
1613 MIM.Action = function (domain, terms)
1614 {
1615   var args = new Array ();
1616   args.push (Xex.CatchTag_.mimtag);
1617   for (var i = 0; i < terms.length; i++)
1618     args.push (terms[i]);
1619   this.action = Xex.Funcall.prototype.New (domain, 'catch', null, args);
1620 }
1621
1622 MIM.Action.prototype.Run = function (domain)
1623 {
1624   var result = this.action.Eval (domain);
1625   if (result.type == 'error')
1626     {
1627       domain.context.Error = result.toString ();
1628       return false;
1629     }
1630   return (result != Xex.CatchTag._mimtag);
1631 }
1632
1633 MIM.Keymap = function ()
1634 {
1635   this.name = 'TOP';
1636   this.submaps = null;
1637   this.actions = null;
1638 };
1639
1640 (function () {
1641   var proto = {};
1642
1643   function add_rule (keymap, rule)
1644   {
1645     var keyseq = rule.keyseq;
1646     var len = keyseq.val.length;
1647     var name = '';
1648
1649     for (var i = 0; i < len; i++)
1650       {
1651         var key = keyseq.val[i];
1652         var sub = false;
1653
1654         name += key.key;
1655         if (! keymap.submaps)
1656           keymap.submaps = {};
1657         else
1658           sub = keymap.submaps[key.key];
1659         if (! sub)
1660           keymap.submaps[key.key] = sub = new MIM.Keymap ();
1661         keymap = sub;
1662         keymap.name = name;
1663       }
1664     keymap.actions = rule.actions;
1665   }
1666
1667   proto.Add = function (map)
1668   {
1669     var rules = map.rules;
1670     var len = rules.length;
1671
1672     for (var i = 0; i < len; i++)
1673       add_rule (this, rules[i]);
1674   }
1675   proto.Lookup = function (keys, index)
1676   {
1677     var sub;
1678
1679     if (index < keys.val.length && this.submaps
1680         && (sub = this.submaps[keys.val[index].key]))
1681       {
1682         index++;
1683         return sub.Lookup (keys, index);
1684       }
1685     return { map: this, index: index };
1686   }
1687
1688   MIM.Keymap.prototype = proto;
1689 }) ();
1690
1691 MIM.State = function (name)
1692 {
1693   this.name = name;
1694   this.keymap = new MIM.Keymap ();
1695 };
1696
1697 (function () {
1698   var proto = new Xex.Term ('state');
1699
1700   proto.Parser = function (domain, node)
1701   {
1702     var map_list = domain.map_list;
1703     var name = node.attributes['sname'].nodeValue;
1704     if (! name)
1705       throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid map");
1706     var state = new MIM.State (name);
1707     for (node = node.firstElement (); node; node = node.nextElement ())
1708       {
1709         if (node.nodeName == 'title')
1710           state.title = node.firstChild.nodeValue;
1711         else
1712           {
1713             var n = node.firstElement ();
1714             if (node.nodeName == 'branch')
1715               {
1716                 state.keymap.Add (map_list[node.attributes['mname'].nodeValue]);
1717                 state.keymap.actions = Xex.Term.Parse (domain, n, null);
1718               }
1719             else if (node.nodeName == 'state-hook')
1720               state.enter_actions = Xex.Term.Parse (domain, n, null);
1721             else if (node.nodeName == 'catch-all-branch')
1722               state.fallback_actions = Xex.Term.Parse (domain, n, null);
1723           }
1724       }
1725     return state;
1726   }
1727
1728   proto.toString = function ()
1729   {
1730     return '<state sname="' + this.name + '">' + this.keymap + '</state>';
1731   }
1732
1733   MIM.State.prototype = proto;
1734 }) ();
1735
1736 (function () {
1737   function Block (index, term)
1738   {
1739     this.Index = index;
1740     if (term.IsStr)
1741       this.Data = term.val;
1742     else if (term.IsList)
1743       {
1744         this.Data = new Array ();
1745         for (var i = 0; i < term.val.length; i++)
1746           this.Data.push (term.val[i].val);
1747       }
1748   }
1749
1750   Block.prototype.Count = function () { return this.Data.length; }
1751   Block.prototype.get = function (i)
1752   {
1753     return (this.Data instanceof Array ? this.Data[i] : this.Data.charAt (i));
1754   }
1755
1756   MIM.Candidates = function (candidates, column)
1757   {
1758     this.column = column;
1759     this.row = 0;
1760     this.index = 0;
1761     this.total = 0;
1762     this.blocks = new Array ();
1763
1764     for (var i = 0; i < candidates.length; i++)
1765       {
1766         var block = new Block (this.total, candidates[i]);
1767         this.blocks.push (block);
1768         this.total += block.Count ();
1769       }
1770   }
1771
1772   function get_col ()
1773   {
1774     return (this.column > 0 ? this.index % this.column
1775             : this.index - this.blocks[this.row].Index);
1776   }
1777
1778   function prev_group ()
1779   {
1780     var col = get_col.call (this);
1781     var nitems;
1782     if (this.column > 0)
1783       {
1784         this.index -= this.column;
1785         if (this.index >= 0)
1786           nitems = this.column;
1787         else
1788           {
1789             var lastcol = (this.total - 1) % this.column;
1790             this.index = (col < lastcol ? this.total - lastcol + col
1791                           : this.total - 1);
1792             this.row = this.blocks.length - 1;
1793             nitems = lastcol + 1;
1794           }
1795         while (this.blocks[this.row].Index > this.index)
1796           this.row--;
1797       }
1798     else
1799       {
1800         this.row = this.row > 0 ? this.row - 1 : this.blocks.length - 1;
1801         nitems = this.blocks[this.row].Count ();
1802         this.index = (this.blocks[this.row].Index
1803                       + (col < nitems ? col : nitems - 1));
1804       }
1805     return nitems;
1806   }
1807
1808   function next_group ()
1809   {
1810     var col = get_col.call (this);
1811     var nitems;
1812     if (this.column > 0)
1813       {
1814         this.index += this.column - col;
1815         if (this.index < this.total)
1816           {
1817             if (this.index + col >= this.total)
1818               {
1819                 nitems = this.total - this.index;
1820                 this.index = this.total - 1;
1821               }
1822             else
1823               {
1824                 nitems = this.column;
1825                 this.index += col;
1826               }
1827           }
1828         else
1829           {
1830             this.index = col;
1831             this.row = 0;
1832           }
1833         while (this.blocks[this.row].Index > this.index)
1834           this.row++;
1835       }
1836     else
1837       {
1838         this.row = this.row < this.blocks.length - 1 ? this.row + 1 : 0;
1839         nitems = this.blocks[this.row].Count ();
1840         this.index = (this.blocks[this.row].Index
1841                       + (col < nitems ? col : nitems - 1));
1842       }
1843     return nitems;
1844   }
1845
1846   function prev ()
1847   {
1848     if (this.index == 0)
1849       {
1850         this.index = this.total - 1;
1851         this.row = this.blocks.length - 1;
1852       }
1853     else
1854       {
1855         this.index--;
1856         if (this.blocks[this.row].Index > this.index)
1857           this.row--;
1858       }
1859     }
1860
1861   function next ()
1862   {
1863     this.index++;
1864     if (this.index == this.total)
1865       {
1866         this.index = 0;
1867         this.row = 0;
1868       }
1869     else
1870       {
1871         var b = this.blocks[this.row];
1872         if (this.index == b.Index + b.Count ())
1873           this.row++;
1874       }
1875   }
1876
1877   function first ()
1878   {
1879     this.index -= get_col.call (this);
1880     while (this.blocks[this.row].Index > this.index)
1881       this.row--;
1882   }
1883
1884   function last ()
1885   {
1886     var b = this.blocks[this.row];
1887     if (this.column > 0)
1888       {
1889         if (this.index + 1 < this.total)
1890           {
1891             this.index += this.column - get_col.call (this) + 1;
1892             while (b.Index + b.Count () <= this.index)
1893               b = this.blocks[++this.row];
1894           }
1895       }
1896     else
1897       this.index = b.Index + b.Count () - 1;
1898   }
1899
1900   MIM.Candidates.prototype.Current = function ()
1901   {
1902     var b = this.blocks[this.row];
1903     return b.get (this.index - b.Index);
1904   }
1905
1906   MIM.Candidates.prototype.Select = function (selector)
1907   {
1908     if (selector.type == 'selector')
1909       {
1910         switch (selector.val)
1911           {
1912           case '@<': first.call (this); break;
1913           case '@>': last.call (this); break;
1914           case '@-': prev.call (this); break;
1915           case '@+': next.call (this); break;
1916           case '@[': prev_group.call (this); break;
1917           case '@]': next_group.cal (this); break;
1918           default: break;
1919           }
1920         return this.Current ();
1921       }
1922     var col, start, end
1923     if (this.column > 0)
1924       {
1925         col = this.index % this.column;
1926         start = this.index - col;
1927         end = start + this.column;
1928       }
1929     else
1930       {
1931         start = this.blocks[this.row].Index;
1932         col = this.index - start;
1933         end = start + this.blocks[this.row].Count;
1934       }
1935     if (end > this.total)
1936       end = this.total;
1937     this.index += selector.val - col;
1938     if (this.index >= end)
1939       this.index = end - 1;
1940     if (this.column > 0)
1941       {
1942         if (selector.val > col)
1943           while (this.blocks[this.row].Index + this.blocks[this.row].Count
1944                  < this.index)
1945             this.row++;
1946         else
1947           while (this.blocks[this.row].Index > this.index)
1948             this.row--;
1949       }
1950     return this.Current ();
1951   }
1952 }) ();
1953
1954 MIM.im_domain = new Xex.Domain ('input-method', null, null);
1955 MIM.im_domain.DefType (MIM.KeySeq.prototype);
1956 MIM.im_domain.DefType (MIM.Marker.prototype);
1957 MIM.im_domain.DefType (MIM.Selector.prototype);
1958 MIM.im_domain.DefType (MIM.Rule.prototype);
1959 MIM.im_domain.DefType (MIM.Map.prototype);
1960 MIM.im_domain.DefType (MIM.State.prototype);
1961
1962 MIM.im_domain.Trace = function (arg)
1963 {
1964   var node = document.getElementById ('log');
1965   if (node)
1966     node.value = "" + arg + node.value;
1967 };
1968
1969 (function () {
1970   var im_domain = MIM.im_domain;
1971
1972   function Finsert (domain, vari, args)
1973   {
1974     var text;
1975     if (args[0].type == 'integer')
1976       text = String.fromCharCode (args[0].val);
1977     else
1978       text = args[0].val;
1979     domain.context.insert (text, null);
1980   }
1981
1982   function Finsert_candidates (domain, vari, args)
1983   {
1984     var ic = domain.context;
1985     var gsize = domain.variables['candidates_group_size'];
1986     var candidates = new MIM.Candidates (args, gsize ? gsize.Intval : 0);
1987     ic.insert (candidates.Current (), candidates);
1988     return args[0];
1989   }
1990
1991   function Fdelete (domain, vari, args)
1992   {
1993     var ic = domain.context;
1994     var pos = args[0].IsInt ? args[0].Intval : args[0].Position (ic);
1995     ic.del (pos);
1996     return new Xex.Term (ic.del (pos));
1997   }
1998
1999   function Fselect (domain, vari, args)
2000   {
2001     var ic = domain.context;
2002     var can = ic.candidates;
2003
2004     if (can)
2005       {
2006         var candidate = can.Current ();
2007
2008         ic.del (ic.cursor_pos - candidate.length);
2009         candidate = can.Select (args[0]);
2010         ic.insert (candidate, can);
2011       }
2012     return args[0];
2013   }
2014
2015   function Fchar_at (domain, vari, args)
2016   {
2017     return new Xex.Term (args[0].CharAt (domain.context));
2018   }
2019
2020   function Fmove (domain, vari, args)
2021   {
2022     var ic = domain.context;
2023     var pos = args[0].IsInt ? args[0].val : args[0].Position (ic);
2024     ic.move (pos);
2025     return args[0];
2026   }
2027
2028   function Fmark (domain, vari, args)
2029   {
2030     args[0].Mark (domain.context);
2031     return args[0];
2032   }
2033
2034   function Fpushback (domain, vari, args)
2035   {
2036     var arg = (args[0].IsInt ? args[0].Intval
2037                : args[0].IsStr ? new KeySeq (args[0])
2038                : args[0]);
2039     domain.context.pushback (arg)
2040     return args[0];
2041   }
2042
2043   function Fundo  (domain, vari, args)
2044   {
2045     var ic = domain.context;
2046     var n = args.length == 0 ? -2 : args[0].val;
2047     if (n < 0)
2048       ic.keys.val.splice (ic.keys.length + n, -n);
2049     else
2050       ic.keys.val.splice (n, ic.keys.length);
2051     ic.reset ();
2052     return Xex.nil;
2053   }
2054
2055   function Fcommit (domain, vari, args)
2056   {
2057     domain.context.commit ();
2058     return Xex.nil;
2059   }
2060
2061   function Funhandle (domain, vari, args)
2062     {
2063       domain.context.commit ();
2064       return Xex.Fthrow (domain, vari, Xex.CatchTag._mimtag);
2065     }
2066
2067   function Fshift (domain, vari, args)
2068   {
2069     var ic = domain.context;
2070     var state_name = args[0].val;
2071     var state = ic.im.state_list[state_name];
2072     if (! state)
2073       throw ("Unknown state: " + state_name);
2074       ic.shift (state);
2075     return args[0];
2076   }
2077
2078   function Fsurrounding_flag (domain, vari, args)
2079   {
2080     return new Xex.IntTerm (-1);
2081   }
2082
2083   im_domain.DefSubr (Finsert, "insert", false, 1, 1);
2084   im_domain.DefSubr (Finsert_candidates, "insert-candidates", false, 1, 1);
2085   im_domain.DefSubr (Fdelete, "delete", false, 1, 1);
2086   im_domain.DefSubr (Fselect, "select", false, 1, 1);
2087   //im_domain.DefSubr (Fshow, "show-candidates", false, 0, 0);
2088   //im_domain.DefSubr (Fhide, "hide-candidates", false, 0, 0);
2089   im_domain.DefSubr (Fmove, "move", false, 1, 1);
2090   im_domain.DefSubr (Fmark, "mark", false, 1, 1);
2091   im_domain.DefSubr (Fpushback, "pushback", false, 1, 1);
2092   //im_domain.DefSubr (Fpop, "pop", false, 0, 0);
2093   im_domain.DefSubr (Fundo, "undo", false, 0, 1);
2094   im_domain.DefSubr (Fcommit, "commit", false, 0, 0);
2095   im_domain.DefSubr (Funhandle, "unhandle", false, 0, 0);
2096   im_domain.DefSubr (Fshift, "shift", false, 1, 1);
2097   //im_domain.DefSubr (Fshiftback, "shiftback", false, 0, 0);
2098   im_domain.DefSubr (Fchar_at, "char-at", false, 1, 1);
2099   //im_domain.DefSubr (Fkey_count, "key-count", false, 0, 0);
2100   im_domain.DefSubr (Fsurrounding_flag, "surrounding-text-flag", false, 0, 0);
2101 }) ();
2102
2103
2104 (function () {
2105   function get_global_var (vname)
2106   {
2107     if (MIM.im_global.load_status == MIM.LoadStatus.NotLoaded)
2108       MIM.im_global.Load ()
2109     return MIM.im_global.domain.variables[vname];
2110   }
2111
2112   function include (node)
2113   {
2114     node = node.firstElement ();
2115     if (node.nodeName != 'tags')
2116       return null;
2117     
2118     var lang = null, name = null, extra = null;
2119     for (node = node.firstElement (); node; node = node.nextElement ())
2120       {
2121         if (node.nodeName == 'language')
2122           lang = node.firstChild.nodeValue;
2123         else if (node.nodeName == 'name')
2124           name = node.firstChild.nodeValue;
2125         else if (node.nodeName == 'extra-id')
2126           extra = node.firstChild.nodeValue;
2127       }
2128     if (! lang || ! MIM.imlist[lang])
2129       return null;
2130     if (! extra)
2131       {
2132         if (! name || ! (im = MIM.imlist[lang][name]))
2133           return null;
2134       }
2135     else
2136       {
2137         if (! (im = MIM.imextra[lang][extra]))
2138           return null;
2139       }
2140     if (im.load_status != MIM.LoadStatus.Loaded
2141         && (im.load_status != MIM.LoadStatus.NotLoaded || ! im.Load ()))
2142       return null;
2143     return im;
2144   }
2145
2146   var parsers = { };
2147
2148   parsers['description'] = function (node)
2149   {
2150     this.description = node.firstChild.nodeValue;
2151   }
2152   parsers['variable-list'] = function (node)
2153   {
2154     for (node = node.firstElement (); node; node = node.nextElement ())
2155       {
2156         var vname = node.attributes['vname'].nodeValue;
2157         if (this != MIM.im_global)
2158           {
2159             var vari = get_global_var (vname);
2160             if (vari != null)
2161               this.domain.Defvar (vname);
2162           }
2163         vname = Xex.Term.Parse (this.domain, node)
2164       }
2165   }
2166   parsers['command-list'] = function (node)
2167   {
2168   }
2169   parsers['macro-list'] = function (node)
2170   {
2171     for (var n = node.firstElement (); n; n = n.nextElement ())
2172       if (n.nodeName == 'xi:include')
2173         {
2174           var im = include (n);
2175           if (! im)
2176             alert ('inclusion fail');
2177           else
2178             for (var macro in im.domain.functions)
2179               {
2180                 var func = im.domain.functions[macro];
2181                 if (func instanceof Xex.Macro)
2182                   im.domain.CopyFunc (this.domain, macro);
2183               }
2184           n = n.previousSibling;
2185           node.removeChild (n.nextSibling);
2186         }
2187     Xex.Term.Parse (this.domain, node.firstElement (), null);
2188   }
2189   parsers['title'] = function (node)
2190   {
2191     this.title = node.firstChild.nodeValue;
2192   }
2193   parsers['map-list'] = function (node)
2194   {
2195     for (node = node.firstElement (); node; node = node.nextElement ())
2196       {
2197         if (node.nodeName == 'xi:include')
2198           {
2199             var im = include (node);
2200             if (! im)
2201               {
2202                 alert ('inclusion fail');
2203                 continue;
2204               }
2205             for (var mapname in im.map_list)
2206               this.map_list[mapname] = im.map_list[mapname];
2207           }
2208         else
2209           {
2210             var map = Xex.Term.Parse (this.domain, node);
2211             this.map_list[map.name] = map;
2212           }
2213       }
2214   }
2215   parsers['state-list'] = function (node)
2216   {
2217     this.domain.map_list = this.map_list;
2218     for (node = node.firstElement (); node; node = node.nextElement ())
2219       {
2220         if (node.nodeName == 'state')
2221           {
2222             var state = Xex.Term.Parse (this.domain, node);
2223             if (! state.title)
2224               state.title = this.title;
2225             if (! this.initial_state)
2226               this.initial_state = state;
2227             this.state_list[state.name] = state;
2228           }
2229       }
2230     delete this.domain.map_list;
2231   }
2232
2233   MIM.IM = function (lang, name, extra_id, file)
2234   {
2235     this.lang = lang;
2236     this.name = name;
2237     this.extra_id = extra_id;
2238     this.file = file;
2239     this.load_status = MIM.LoadStatus.NotLoaded;
2240     this.domain = new Xex.Domain (this.lang + '-'
2241                                   + (this.name != 'nil'
2242                                      ? this.name : this.extra_id),
2243                                   MIM.im_domain, null);
2244   }
2245
2246   var proto = {
2247     Load: function ()
2248     {
2249       var node = Xex.Load (null, this.file);
2250       if (! node)
2251         {
2252           this.load_status = MIM.LoadStatus.Error;
2253           return false;
2254         }
2255       this.map_list = {};
2256       this.initial_state = null;
2257       this.state_list = {};
2258       for (node = node.firstElement (); node; node = node.nextElement ())
2259         {
2260           var name = node.nodeName;
2261           var parser = parsers[name];
2262           if (parser)
2263             parser.call (this, node);
2264         }
2265       this.load_status = MIM.LoadStatus.Loaded;
2266       return true;
2267     }
2268   }
2269
2270   MIM.IM.prototype = proto;
2271
2272   MIM.IC = function (im, target)
2273   {
2274     if (im.load_status == MIM.LoadStatus.NotLoaded)
2275       im.Load ();
2276     if (im.load_status != MIM.LoadStatus.Loaded)
2277       alert ('im:' + im.name + ' error:' + im.load_status);
2278     this.im = im;
2279     this.target = target;
2280     this.domain = new Xex.Domain ('context', im.domain, this);
2281     this.active = true;
2282     this.reset ();
2283     this.spot = 0;
2284   }
2285
2286   MIM.CandidateTable = function ()
2287   {
2288     this.table = new Array ();
2289   }
2290
2291   MIM.CandidateTable.prototype.get = function (from)
2292   {
2293     for (var i = 0; i < this.table.length; i++)
2294       {
2295         var elt = this.table[i];
2296         if (elt.from <= from && elt.to > from)
2297           return elt.val;
2298       }
2299   }
2300
2301   MIM.CandidateTable.prototype.put = function (from, to, candidates)
2302   {
2303     for (var i = 0; i < this.table.length; i++)
2304       {
2305         var elt = this.table[i];
2306         if (elt.from >= from && elt.from < to
2307             || elt.to >= from && elt.to < to)
2308           {
2309             elt.from = from;
2310             elt.to = to;
2311             elt.val = candidates;
2312             return;
2313           }
2314       }
2315     this.table.push ({ from: from, to: to, val: candidates });
2316   }
2317
2318   MIM.CandidateTable.prototype.adjust = function (from, to, inserted)
2319   {
2320     var diff = inserted - (to - from);
2321     for (var i = 0; i < this.table.length; i++)
2322       {
2323         var elt = this.table[i];
2324         if (elt.from >= to)
2325           {
2326             elt.from += diff;
2327             elt.to += diff;
2328           }
2329       }
2330   }
2331
2332   MIM.CandidateTable.prototype.clear = function ()
2333   {
2334     this.table.length = 0;
2335   }
2336
2337   function detach_candidates (ic)
2338   {
2339     ic.candidate_table.clear ();
2340     ic.candidates = null;
2341     ic.changed |= (MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos
2342                    | ChangedStatus.CandidateList
2343                    | ChangedStatus.CandidateIndex
2344                    | ChangedStatus.CandidateShow);
2345   }
2346
2347   function set_cursor (prefix, pos)
2348   {
2349     this.cursor_pos = pos;
2350     if (pos > 0)
2351       this.candidates = this.candidate_table.get (pos - 1);
2352     else
2353       this.candidates = null;
2354   }
2355
2356   function save_state ()
2357   {
2358     this.state_var_values = this.domain.SaveValues ();
2359     this.state_preedit = this.preedit;
2360     this.state_key_head = this.key_head;
2361     this.state_pos = this.cursor_pos;
2362   }
2363
2364   function restore_state ()
2365   {
2366     this.domain.RestoreValues (this.state_var_values);
2367     this.preedit = this.state_preedit;
2368     set_cursor.call (this, "restore", this.state_pos);
2369   }
2370
2371   function handle_key ()
2372   {
2373     var out = this.keymap.Lookup (this.keys, this.key_head);
2374     var sub = out.map;
2375     var branch_actions = this.state.keymap.actions;
2376
2377     this.domain.Trace ('handling ' + this.keys.val[this.key_head]
2378                        + ' in ' + this.state.name + ':'
2379                        + this.keymap.name + "\n");
2380     this.key_head = out.index;
2381     if (sub != this.keymap)
2382       {
2383
2384         restore_state.call (this);
2385         this.keymap = sub;
2386         this.domain.Trace ('submap found\n');
2387         if (this.keymap.actions)
2388           {
2389             this.domain.Trace ('taking map actions:\n');
2390             if (! this.take_actions (this.keymap.actions))
2391               return false;
2392           }
2393         else if (this.keymap.submaps)
2394           {
2395             MIM.log ('no map actions');
2396             for (var i = this.state_key_head; i < this.key_head; i++)
2397               {
2398                 MIM.log ('inserting key:' + this.keys.val[i].key);
2399                 this.insert (this.keys.val[i].key, null);
2400               }
2401           }
2402         if (! this.keymap.submaps)
2403           {
2404             MIM.log ('terminal:');
2405             if (this.keymap.branch_actions != null)
2406               {
2407                 MIM.log ('branch actions:');
2408                 if (! this.take_actions (branch_actions))
2409                   return false;
2410               }
2411             if (this.keymap != this.state.keymap)
2412               this.shift (this.state);
2413           }
2414       }
2415     else
2416       {
2417         MIM.log ('no submap');
2418         var current_state = this.state;
2419         var map = this.keymap;
2420
2421         if (branch_actions)
2422           {
2423             MIM.log ('branch actions');
2424             if (! this.take_actions (this.keymap.branch_actions))
2425               return false;
2426           }
2427
2428         if (map == this.keymap)
2429           {
2430             MIM.log ('no state change');
2431             if (map == this.initial_state.keymap
2432                 && this.key_head < this.keys.val.length)
2433               {
2434                 MIM.log ('unhandled');
2435                 return false;
2436               }
2437             if (this.keymap != current_state.keymap)
2438               this.shift (current_state);
2439             else if (this.keymap.actions == null)
2440               this.shift (this.initial_state);
2441           }
2442       }
2443     return true;
2444   }
2445
2446   proto = {
2447     reset: function ()
2448     {
2449       this.produced = null;
2450       this.preedit = '';
2451       this.preedit_saved = '';
2452       this.cursor_pos = 0;
2453       this.marker_positions = {};
2454       this.candidates = null;
2455       this.candidate_show = false;
2456       this.state = null;
2457       this.prev_state = null;
2458       this.initial_state = this.im.initial_state;
2459       this.title = this.initial_state.title;
2460       this.state_preedit = '';
2461       this.state_key_head = 0;
2462       this.state_var_values = {};
2463       this.state_pos = 0;
2464       this.keymap = null;
2465       this.keys = new MIM.KeySeq ();
2466       this.key_head = 0;
2467       this.key_unhandled = false;
2468       this.unhandled_key = null;
2469       this.changed = MIM.ChangedStatus.None;
2470       this.error_message = '';
2471       this.title = this.initial_state.title;
2472       this.produced = '';
2473       this.preedit = '';
2474       this.preedit_saved = '';
2475       this.marker_positions = {};
2476       this.candidate_table = new MIM.CandidateTable ();
2477       this.candidates = null;
2478       this.candidate_show = false;
2479       this.shift (this.initial_state);
2480     },
2481
2482     catch_args: new Array (Xex.CatchTag._mimtag, null),
2483
2484     take_actions: function (actions)
2485     {
2486       var func_progn = this.domain.GetFunc ('progn');
2487       var func_catch = this.domain.GetFunc ('catch');
2488       this.catch_args[1] = new Xex.Funcall (func_progn, null, actions);
2489       var term = new Xex.Funcall (func_catch, null, this.catch_args);
2490       term = term.Eval (this.domain);
2491       return (! term.IsSymbol || term.val != '@mimtag');
2492     },
2493
2494     GetSurroundingChar: function (pos)
2495     {
2496       if (pos < 0 ? this.caret_pos < - pos : this.target.value.length < pos)
2497         return 0;
2498       return this.target.value.charCodeAt (this.caret_pos + pos);
2499     },
2500     
2501     adjust_markers: function (from, to, inserted)
2502     {
2503       var diff = inserted - (to - from);
2504
2505       for (var m in this.marker_positions)
2506         if (this.marker_positions[m] > from)
2507           this.marker_positions[m] = (this.marker_positions[m] >= to
2508                                       ? pos + diff : from);
2509       if (this.cursor_pos >= to)
2510         set_cursor.call (this, 'adjust', this.cursor_pos + diff);
2511       else if (this.cursor_pos > from)
2512         set_cursor.call (this, 'adjust', from)
2513     },
2514
2515     preedit_replace: function (from, to, text, candidates)
2516     {
2517       this.preedit = (this.preedit.substring (0, from)
2518                       + text + this.preedit.substring (to));
2519       this.adjust_markers (from, to, text.length);
2520       this.candidate_table.adjust (from, to, text.length);
2521       if (candidates)
2522         this.candidate_table.put (from, from + text.length, candidates)
2523     },
2524
2525     insert: function (text, candidates)
2526     {
2527       this.preedit_replace (this.cursor_pos, this.cursor_pos, text, candidates);
2528       this.changed = MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos;
2529     },
2530
2531     del: function (pos)
2532     {
2533       var deleted = pos - this.cursor_pos;
2534       if (pos < 0)
2535         {
2536           this.DelSurroundText (pos);
2537           pos = 0;
2538         }
2539       else if (pos > this.preedit.length)
2540         {
2541           this.DelSurroundText (pos - this.preedit.length);
2542           pos = this.preedit.length;
2543         }
2544       if  (pos < this.cursor_pos)
2545         this.preedit = (this.predit.substring (0, pos)
2546                         + this.preedit.substring (this.cursor_pos));
2547       else
2548         this.preedit = (this.preedit.substring (0, this.cursor_pos)
2549                         + this.predit.substring (pos));
2550       return deleted;
2551     },
2552
2553     show: function ()
2554     {
2555       this.candidate_show = true;
2556       this.changed |= MIM.ChangedStatus.CandidateShow;
2557     },
2558
2559     hide: function ()
2560     {
2561       this.candidate_show = false;
2562       this.changed |= MIM.ChangedStatus.CandidateShow;
2563     },
2564
2565     move: function (pos)
2566     {
2567       if (pos < 0)
2568         pos = 0;
2569       else if (pos > this.preedit.length)
2570         pos = this.preedit.length;
2571       if (pos != this.cursor_pos)
2572         {
2573           set_cursor.call (this, 'move', pos);
2574           this.changed |= MIM.ChangedStatus.Preedit;
2575         }
2576     },
2577
2578     pushback: function (n)
2579     {
2580       if (n instanceof MIM.KeySeq)
2581         {
2582           if (this.key_head > 0)
2583             this.key_head--;
2584           if (this.key_head < this.keys.val.length)
2585             this.keys.val.splice (this.key_head,
2586                                   this.keys.val.length - this.key_head);
2587           for (var i = 0; i < n.val.length; i++)
2588             this.keys.val.push (n.val[i]);
2589           return;
2590         }
2591       if (n > 0)
2592         {
2593           this.key_head -= n;
2594           if (this.key_head < 0)
2595             this.key_head = 0;
2596         }
2597       else if (n == 0)
2598         this.key_head = 0;
2599       else
2600       {
2601         this.key_head = - n;
2602         if (this.key_head > this.keys.val.length)
2603           this.key_head = this.keys.val.length;
2604       }
2605     },
2606
2607     pop: function ()
2608     {
2609       if (this.key_head < this.keys.val.length)
2610         this.keys.val.splice (this.key_head, 1);
2611     },
2612
2613     commit: function ()
2614     {
2615       if (this.preedit.length > 0)
2616       {
2617         this.candidate_table.clear ();
2618         this.produced += this.preedit;
2619         this.preedit_replace.call (this, 0, this.preedit.length, '', null);
2620       }
2621     },
2622
2623     shift: function (state)
2624     {
2625       if (state == null)
2626         {
2627           MIM.log ("shifting back to previous");
2628           if (this.prev_state == null)
2629             return;
2630           state = this.prev_state;
2631         }
2632       else
2633         MIM.log ("shifting to " + state.name);
2634
2635       if (state == this.initial_state)
2636         {
2637           if (this.state)
2638             {
2639               this.commit ();
2640               this.keys.val.splice (0, this.key_head);
2641               this.key_head = 0;
2642               this.prev_state = null;
2643             }
2644         }
2645       else
2646         {
2647           if (state != this.state)
2648             this.prev_state = this.state;
2649         }
2650       if (state != this.state && state.enter_actions)
2651         this.take_actions (state.enter_actions);
2652       if (! this.state || this.state.title != state.title)
2653         this.changed |= MIM.ChangedStatus.StateTitle;
2654       this.state = state;
2655       this.keymap = state.keymap;
2656       this.state_key_head = this.key_head;
2657       save_state.call (this);
2658     },
2659
2660     Filter: function (key)
2661     {
2662       if (! this.active)
2663         {
2664           this.key_unhandled = true;
2665           this.unhandled_key = key;
2666           return false;
2667         }
2668       if (key.key == '_reload')
2669         return true;
2670       this.changed = MIM.ChangedStatus.None;
2671       this.produced = '';
2672       this.key_unhandled = false;
2673       this.keys.val.push (key);
2674       var count = 0;
2675       while (this.key_head < this.keys.val.length)
2676         {
2677           if (! handle_key.call (this))
2678             {
2679               if (this.key_head < this.keys.val.length)
2680                 {
2681                   this.unhandled_key = this.keys.val[this.key_head];
2682                   this.keys.val.splice (this.key_head, this.key_head + 1);
2683                 }
2684               this.key_unhandled = true;
2685               break;
2686             }
2687           if (++count == 10)
2688             {
2689               this.reset ();
2690               this.key_unhandled = true;
2691               break;
2692             }
2693         }
2694       if (this.key_unhandled)
2695         {
2696           this.keys.val.length = 0;
2697           this.key_head = this.state_key_head = this.commit_key_head = 0;
2698         }
2699       return (! this.key_unhandled
2700               && this.produced.length == 0
2701               && this.preedit.length == 0);
2702     }
2703   }
2704
2705   MIM.IC.prototype = proto;
2706
2707   var node = Xex.Load (null, "imlist.xml");
2708   for (node = node.firstChild; node; node = node.nextSibling)
2709     if (node.nodeName == 'input-method')
2710       {
2711         var lang = null, name = null, extra_id = null, file = null;
2712
2713         for (var n = node.firstChild; n; n = n.nextSibling)
2714           {
2715             if (n.nodeName == 'language')
2716               lang = n.firstChild.nodeValue;
2717             else if (n.nodeName == 'name')
2718               name = n.firstChild.nodeValue;
2719             else if (n.nodeName == 'extra-id')
2720               extra_id = n.firstChild.nodeValue;
2721             else if (n.nodeName == 'filename')
2722               file = n.firstChild.nodeValue;
2723           }
2724         if (name && name != 'nil')
2725           {
2726             if (! MIM.imlist[lang])
2727               MIM.imlist[lang] = {};
2728             MIM.imlist[lang][name] = new MIM.IM (lang, name, extra_id, file);
2729           }
2730         else if (extra_id && extra_id != 'nil')
2731           {
2732             if (! MIM.imextra[lang])
2733               MIM.imextra[lang] = {};
2734             MIM.imextra[lang][extra_id] = new MIM.IM (lang, name, extra_id, file);
2735           }
2736       }
2737   if (MIM.imextra.t && MIM.imextra.t.global)
2738     MIM.im_global = MIM.imextra.t.global;
2739   else
2740     {
2741       MIM.im_global = new MIM.IM ('t', 'nil', 'global', null);
2742       MIM.im_global.load_status = MIM.LoadStatus.Error;
2743     }
2744   node = undefined;
2745 }) ();
2746
2747 (function () {
2748   var keys = new Array ();
2749   keys[0x09] = 'tab';
2750   keys[0x08] = 'backspace';
2751   keys[0x0D] = 'return';
2752   keys[0x1B] = 'escape';
2753   keys[0x20] = 'space';
2754   keys[0x21] = 'pageup';
2755   keys[0x22] = 'pagedown';
2756   keys[0x23] = 'end';
2757   keys[0x24] = 'home';
2758   keys[0x25] = 'left';
2759   keys[0x26] = 'up';
2760   keys[0x27] = 'right';
2761   keys[0x28] = 'down';
2762   keys[0x2D] = 'insert';
2763   keys[0x2E] = 'delete';
2764   for (var i = 1; i <= 12; i++)
2765     keys[111 + i] = "f" + i;
2766   keys[0x90] = "numlock";
2767   keys[0xF0] = "capslock";
2768
2769   MIM.decode_key_event = function (event)
2770   {
2771     var key = ((event.type == 'keydown' || event.keyCode) ? event.keyCode
2772                : event.charCode ? event.charCode
2773                : false);
2774     if (! key)
2775       return false;
2776     if (event.type == 'keydown')
2777       {
2778         key = keys[key];
2779         if (! key)
2780           return false;
2781         if (event.shiftKey) key = "S-" + key ;
2782       }
2783     else
2784       key = String.fromCharCode (key);
2785     if (event.altKey) key = "A-" + key ;
2786     if (event.ctrlKey) key = "C-" + key ;
2787     return new MIM.Key (key);
2788   }
2789 }) ();
2790
2791 MIM.add_event_listener
2792   = (window.addEventListener
2793      ? function (target, type, listener) {
2794        target.addEventListener (type, listener, false);
2795      }
2796      : window.attachEvent
2797      ? function (target, type, listener) {
2798        target.attachEvent ('on' + type,
2799                            function() {
2800                              listener.call (target, window.event);
2801                            });
2802      }
2803      : function (target, type, listener) {
2804        target['on' + type]
2805          = function (e) { listener.call (target, e || window.event); };
2806      });
2807
2808 MIM.log = function (msg)
2809 {
2810   var node = document.getElementById ('log');
2811   node.value = msg + "\n" + node.value;
2812 }
2813
2814 MIM.debug_print = function (event, ic)
2815 {
2816   if (! MIM.debug)
2817     return;
2818   if (! MIM.debug_nodes)
2819     {
2820       MIM.debug_nodes = new Array ();
2821       MIM.debug_nodes['keydown'] = document.getElementById ('keydown');
2822       MIM.debug_nodes['keypress'] = document.getElementById ('keypress');
2823       MIM.debug_nodes['status0'] = document.getElementById ('status0');
2824       MIM.debug_nodes['status1'] = document.getElementById ('status1');
2825       MIM.debug_nodes['keymap0'] = document.getElementById ('keymap0');
2826       MIM.debug_nodes['keymap1'] = document.getElementById ('keymap1');
2827       MIM.debug_nodes['preedit0'] = document.getElementById ('preedit0');
2828       MIM.debug_nodes['preedit1'] = document.getElementById ('preedit1');
2829     }
2830   var target = event.target;
2831   var code = event.keyCode;
2832   var ch = event.type == 'keydown' ? 0 : event.charCode;
2833   var key = MIM.decode_key_event (event);
2834   var index;
2835
2836   MIM.debug_nodes[event.type].innerHTML = "" + code + "/" + ch + " : " + key;
2837   index = (event.type == 'keydown' ? '0' : '1');
2838   if (ic)
2839     MIM.debug_nodes['status' + index].innerHTML = ic.im.load_status;
2840   else
2841     MIM.debug_nodes['status' + index].innerHTML = 'no IM';
2842   MIM.debug_nodes['keymap' + index].innerHTML = ic.state.name;
2843   MIM.debug_nodes['preedit' + index].innerHTML = ic.preedit;
2844 };
2845
2846 MIM.get_range = function (target, range)
2847 {
2848   if (target.selectionStart != null) // for Mozilla
2849     {
2850       range[0] = target.selectionStart;
2851       range[1] = target.selectionEnd;
2852     }
2853   else                          // for IE
2854     {
2855       var r = document.selection.createRange ();
2856       var rr = r.duplicate ();
2857
2858       rr.moveToElementText (target);
2859       rr.setEndPoint ('EndToEnd', range);
2860       range[0] = rr.text.length - r.text.length;
2861       range[1] = rr.text.length;
2862     }
2863 }
2864
2865 MIM.set_caret = function (target, ic)
2866 {
2867   if (target.setSelectionRange) // Mozilla
2868     {
2869       var scrollTop = target.scrollTop;
2870       target.setSelectionRange (ic.spot, ic.spot + ic.preedit.length);
2871       target.scrollTop = scrollTop;
2872     }
2873   else                          // IE
2874     {
2875       var range = target.createTextRange ();
2876       range.moveStart ('character', ic.spot);
2877       range.moveEnd ('character', ic.spot + ic.preedit.length);
2878       range.select ();
2879     }
2880 };
2881
2882 (function () {
2883   var range = new Array ();
2884
2885   MIM.check_range = function (target, ic)
2886   {
2887     MIM.get_range (target, range);
2888     if (range[0] != ic.spot || range[1] - range[0] != ic.preedit.length
2889         || target.value.substring (range[0], range[1]) != ic.preedit)
2890       {
2891         MIM.log ('reset:' + ic.spot + '-' + (ic.spot + ic.preedit.length)
2892                  + '/' + range[0] + '-' + range[1]);
2893         ic.reset ();
2894       }
2895     target.value = (target.value.substring (0, range[0])
2896                     + target.value.substring (range[1]));
2897     ic.spot = range[0];
2898   }
2899 }) ();
2900
2901 MIM.update = function (target, ic)
2902 {
2903   var text = target.value;
2904   target.value = (text.substring (0, ic.spot)
2905                   + ic.produced
2906                   + ic.preedit
2907                   + text.substring (ic.spot));
2908   ic.spot += ic.produced.length;
2909   MIM.set_caret (target, ic);
2910 };
2911
2912 MIM.reset_ic = function (event)
2913 {
2914   if (event.target.mim_ic)
2915     {
2916       var ic = event.target.mim_ic;
2917       var pos = ic.spot + ic.preedit.length;
2918       ic.reset ();
2919       if (pos > ic.spot)
2920         event.target.setSelectionRange (pos, pos);
2921     }
2922 };
2923
2924 MIM.keydown = function (event)
2925 {
2926   var target = event.target;
2927   if (! (target.type == "text" || target.type == "textarea"))
2928     return;
2929
2930   var ic = target.mim_ic;
2931   if (! ic || ic.im != MIM.current)
2932     {
2933       MIM.log ('creating IC');
2934       ic = new MIM.IC (MIM.current, target);
2935       target.mim_ic = ic;
2936       MIM.add_event_listener (target, 'blur', MIM.reset_ic);
2937     }
2938   if (ic.im.load_status != MIM.LoadStatus.Loaded)
2939     return;
2940   MIM.check_range (target, ic);
2941   MIM.debug_print (event, ic);
2942   ic.key = MIM.decode_key_event (event);
2943 };
2944
2945 MIM.keypress = function (event)
2946 {
2947   if (! (event.target.type == "text" || event.target.type == "textarea"))
2948     return;
2949
2950   var ic = event.target.mim_ic;
2951   var i;
2952
2953   try {
2954     if (ic.im.load_status != MIM.LoadStatus.Loaded)
2955       return;
2956     if (! ic.key)
2957       ic.key = MIM.decode_key_event (event);
2958     if (! ic.key)
2959       {
2960         ic.reset ();
2961         return;
2962       }
2963     
2964     MIM.log ("filtering " + ic.key);
2965     var result = ic.Filter (ic.key);
2966     MIM.update (event.target, ic);
2967     if (! ic.key_unhandled)
2968       event.preventDefault ();
2969   } finally {
2970     MIM.debug_print (event, ic);
2971   }
2972   return;
2973 };
2974
2975 MIM.select_im = function (event)
2976 {
2977   var target = event.target.parentNode;
2978   while (target.tagName != "SELECT")
2979     target = target.parentNode;
2980   var idx = 0;
2981   var im = false;
2982   for (var lang in MIM.imlist)
2983     for (var name in MIM.imlist[lang])
2984       if (idx++ == target.selectedIndex)
2985         {
2986           im = MIM.imlist[lang][name];
2987           break;
2988         }
2989   document.getElementsByTagName ('body')[0].removeChild (target);
2990   target.target.focus ();
2991   if (im && im != MIM.current)
2992     {
2993       MIM.current = im;
2994       MIM.log ('select IM: ' + im.name);
2995     }
2996 };
2997
2998 MIM.destroy_menu = function (event)
2999 {
3000   if (event.target.tagName == "SELECT")
3001     document.getElementsByTagName ('body')[0].removeChild (event.target);
3002 };
3003
3004 MIM.select_menu = function (event)
3005 {
3006   var target = event.target;
3007
3008   if (! ((target.type == "text" || target.type == "textarea")
3009          && event.which == 1 && event.ctrlKey))
3010     return;
3011
3012   var sel = document.createElement ('select');
3013   sel.onclick = MIM.select_im;
3014   sel.onmouseout = MIM.destroy_menu;
3015   sel.style.position='absolute';
3016   sel.style.left = (event.clientX - 10) + "px";
3017   sel.style.top = (event.clientY - 10) + "px";
3018   sel.target = target;
3019   var idx = 0;
3020   for (var lang in MIM.imlist)
3021     for (var name in MIM.imlist[lang])
3022       {
3023         var option = document.createElement ('option');
3024         var imname = lang + "-" + name;
3025         option.appendChild (document.createTextNode (imname));
3026         option.value = imname;
3027         sel.appendChild (option);
3028         if (MIM.imlist[lang][name] == MIM.current)
3029           sel.selectedIndex = idx;
3030         idx++;
3031       }
3032   sel.size = idx;
3033   document.getElementsByTagName ('body')[0].appendChild (sel);
3034 };
3035
3036 MIM.test = function ()
3037 {
3038   var im = MIM.imlist['t']['latn-post'];
3039   var ic = new MIM.IC (im, null);
3040
3041   ic.Filter (new MIM.Key ('a'));
3042   ic.Filter (new MIM.Key ("'"));
3043
3044   if (true)
3045     document.getElementById ('text').value = ic.produced + ic.preedit;
3046   else {
3047     try {
3048       document.getElementById ('text').value
3049         = Xex.Term.Parse (domain, body).Eval (domain).toString ();
3050     } catch (e) {
3051       if (e instanceof Xex.ErrTerm)
3052         alert (e);
3053       throw e;
3054     }
3055   }
3056 }
3057
3058
3059 MIM.init = function ()
3060 {
3061   MIM.add_event_listener (window, 'keydown', MIM.keydown);
3062   MIM.add_event_listener (window, 'keypress', MIM.keypress);
3063   MIM.add_event_listener (window, 'mousedown', MIM.select_menu);
3064   if (window.location == 'http://localhost/mim/index.html')
3065     MIM.server = 'http://localhost/mim';
3066   MIM.current = MIM.imlist['vi']['telex'];
3067 };
3068
3069 MIM.init_debug = function ()
3070 {
3071   MIM.debug = true;
3072   MIM.init ();
3073 };