*** empty log message ***
[m17n/m17n-lib-js.git] / xex.js
1 // -* coding: utf-8; -*
2
3 var Xex = {};
4
5 Xex.Alist = function ()
6 {
7   this.count = 0;
8 }
9
10 Xex.Alist.prototype.put = function (key, val)
11 {
12   this.count++;
13   return (this[key] = val);
14 }
15 Xex.Alist.prototype.clone = function ()
16 {
17   var alist = new Xex.Alist ();
18   for (key in this)
19     alist[key] = this[key];
20   return alist;
21 }
22 Xex.Alist.prototype.toString = function ()
23 {
24   var str = 'alist:';
25   for (key in this)
26     str += '"' + key + '"';
27   return str;
28 }
29
30 // Xex.alist = new Xex.Alist ();
31 // Xex.alist.put ('abc', "ABC");
32 // alert (Xex.alist['abc']);
33
34 Xex.Error = {
35   UnknownError: "unknown-error",
36   WrongArgument: "wrong-argument",
37   // Load time errors.
38   InvalidInteger: "invalid-integer",
39   TermTypeInvalid: "term-type-invalid",
40   FunctionConflict: "function-conflict",
41   VariableTypeConflict: "variable-type-conflict",
42   VariableRangeConflict: "variable-range-conflict",
43   VariableWrongRange: "variable-wrong-range",
44   VariableWrongValue: "variable-wrong-value",
45
46   UnknownFunction: "unknown-function",
47   MacroExpansionError: "macro-expansion-error",
48   NoVariableName: "no-variable-anme",
49
50   // Run time errors.
51   ArithmeticError: "arithmetic-error",
52   WrongType: "wrong-type",
53   IndexOutOfRange: "index-out-of-range",
54   ValueOutOfRange: "value-out-of-range",
55   NoLoopToBreak: "no-loop-to-break",
56   UncaughtThrow: "uncaught-throw"
57 };
58
59 Xex.Variable = function (domain, name, val)
60 {
61   this.domain = domain;
62   this.name = name;
63   this.val = val;
64 }
65
66 Xex.Variable.prototype.clone = function () {
67   return new Xex.Variable (this.domain, this.name, this.value);
68 }
69     
70 Xex.Variable.prototype.Equals = function (obj) {
71   return ((obj instanceof Xex.Variable)
72           && obj.name == this.name);
73 }
74
75 Xex.Variable.prototype.SetValue = function (term) {
76   this.val = term;
77   return term;
78 }
79
80 Xex.Function = function (name, with_var, min_args, max_args) {
81   this.name = name;
82   this.with_var = with_var;
83   this.min_args = min_args;
84   this.max_args = max_args;
85 };  
86
87 Xex.Subrountine = function (builtin, name, with_var, min_args, max_args) {
88   this.name = name;
89   this.with_var = with_var;
90   this.min_args = min_args;
91   this.max_args = max_args;
92   this.builtin = builtin;
93 }
94
95 Xex.Subrountine.prototype.Call = function (domain, vari, args)
96 {
97   newargs = new Array ();
98   for (var i = 0; i < args.length; i++)
99     {
100       newargs[i] = args[i].Eval (domain);
101       if (domain.Thrown ())
102         return newargs[i];
103     }
104   return this.builtin (domain, vari, newargs)
105 }
106
107 Xex.SpecialForm = function (builtin, name, with_var, min_args, max_args)
108 {
109   this.name = name;
110   this.with_var = with_var;
111   this.min_args = min_args;
112   this.max_args = max_args;
113   this.builtin = builtin;
114 }
115
116 Xex.SpecialForm.prototype.Call = function (domain, vari, args)
117 {
118   return this.builtin (domain, vari, args)
119 }
120
121 Xex.Lambda = function (name, min_args, max_args, args, body)
122 {
123   this.name = name;
124   this.with_var = with_var;
125   this.min_args = min_args;
126   this.max_args = max_args;
127   this.args = args;
128   this.body = body;
129 }
130
131 Xex.Lambda.prototype.Call = function (domain, vari, args)
132 {
133   var current = domain.bindings;
134   var result = Xex.Zero;
135   var limit = max_args >= 0 ? args.length : args.length - 1;
136   var i;
137   
138   try {
139     for (i = 0; i < limit; i++)
140       {
141         result = args[i].Eval (domain);
142         if (domain.Thrown ())
143           return result;
144         domain.Bind (this.args[i], result);
145       }
146     if (max_args < 0)
147       {
148         var list = new Array ();
149         for (i = 0; i < args[limit].length; i++)
150           {
151             result = args[limit].Eval (domain);
152             if (domain.Thrown ())
153               return result;
154             list[i] = result;
155           }
156         domain.Bind (this.args[limit], list);
157       }
158     try {
159       domain.Catch (Xex.CatchTag.Return);
160       for (var term in this.body)
161         {
162           result = term.Eval (domain);
163           if (domain.Thrown ())
164             return result;
165         }
166     } finally {
167       domain.Uncatch ();
168     }
169   } finally {
170     domain.UnboundTo (current);
171   }
172   return result;
173 }
174
175 Xex.Macro = function (name, min_args, max_args, args, body)
176 {
177   this.name = name;
178   this.with_var = with_var;
179   this.min_args = min_args;
180   this.max_args = max_args;
181   this.args = args;
182   this.body = body;
183 }
184
185 Xex.Macro.prototype.Call = function (domain, vari, args)
186 {
187   var current = domain.bindings;
188   var result = Xex.Zero;
189   var i;
190
191   try {
192     for (i = 0; i < args.length; i++)
193       domain.Bind (this.args[i], args[i]);
194     try {
195       domain.Catch (Xex.CatchTag.Return);
196       for (var term in body)
197         {
198           result = term.Eval (domain);
199           if (domain.Thrown ())
200             break;
201         }
202     } finally {
203       domain.Uncatch ();
204     }
205   } finally {
206     domain.UnboundTo (current);
207   }
208   return result;
209 }
210
211 Xex.Bindings = function (vari)
212 {
213   this.vari = vari;
214   this.old_value = vari.val;
215 }
216
217 Xex.Bindings.prototype.UnboundTo = function (boundary)
218 {
219   for (var b = this; b != boundary; b = b.next)
220     b.vari.val = b.old_value;
221   return boundary;
222 }
223
224 Xex.Bind = function (bindings, vari, val)
225 {
226   var b = new Xex.Bindings (vari);
227   b.vari.val = val;
228   b.next = bindings;
229   return b;
230 }
231
232 Xex.CatchTag = {
233   Return: 0,
234   Break: 1
235 }
236
237 Xex.Domain = function (name, parent, context)
238 {
239   this.name = name;
240   this.context = context;
241   this.depth = 0;
242
243   if (name != 'basic' && ! parent)
244     parent = Xex.BasicDomain
245   this.parent = parent;
246   this.termtypes = {};
247   this.functions = {};
248   this.variables = {};
249   if (parent)
250     {
251       var elt;
252       for (elt in parent.termtypes)
253         this.termtypes[elt] = parent.termtypes[elt];
254       for (elt in parent.functions)
255         this.functions[elt] = parent.functions[elt];
256       for (elt in parent.variables)
257         this.variables[elt] = parent.variables[elt];
258     }
259
260   this.call_stack = new Array ();
261   this.bindings = null;
262   this.catch_stack = new Array ();
263   this.catch_count = 0;
264   this.caught = false;
265 };
266
267 Xex.Domain.prototype = {
268   CallStackCount: function () { return this.call_stack.length; },
269   CallStackPush: function (term) { this.call_stack.push (term); },
270   CallStackPop: function () { this.call_stack.pop (); },
271   Bind: function (vari, val)
272   {
273     this.bindings = Xex.Bind (this.bindings, vari, val);
274   },
275   UnboundTo: function (boundary)
276   {
277     if (this.bindings)
278       this.bindings = this.bindings.UnboundTo (boundary);
279   },
280   Catch: function (tag) { this.catch_stack.push (tag); this.catch_count++; },
281   Uncatch: function ()
282   {
283     this.catch_stack.pop ();
284     if (this.catch_count > this.catch_stack.length)
285       this.catch_count--;
286   },
287   Thrown: function ()
288   {
289     if (this.catch_count < this.catch_stack.length)
290       {
291         this.caught = (this.catch_count == this.catch_stack.length - 1);
292         return true;
293       }
294     this.caught = false;
295     return false;
296   },
297   ThrowReturn: function ()
298   {
299     for (var i = this.catch_stack.length - 1; i >= 0; i--)
300       {
301         this.catch_count--;
302         if (this.catch_stack[i] == Xex.CatchTag.Return)
303           break;
304       }
305   },
306   ThrowBreak: function ()
307   {
308     if (this.catch_stack[this.catch_stack.length - 1] != Xex.CatchTag.Break)
309       throw new Xex.ErrTerm (Xex.Error.NoLoopToBreak,
310                            "No surrounding loop to break");
311     this.catch_count--;
312   },
313   ThrowSymbol: function (tag)
314   {
315     var i = this.catch_count;
316     for (var j = this.catch_stack.length - 1; j >= 0; j--)
317       {
318         i--;
319         if (Xex.CatchTag.Matches (this.catch_stack[i], tag))
320           {
321             this.catch_count = i;
322             return;
323           }
324       }
325     throw new Xex.ErrTerm (Xex.Error.UncaughtThrow,
326                          "No corresponding catch: " + tag);
327   },
328   DefType: function (obj)
329   {
330     var type = obj.type;
331     if (this.termtypes[type])
332       throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
333                            "Already defined: " + type);
334     if (this.functions[type])
335       throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
336                            "Already defined as a funciton or a macro: "
337                            + type);
338     this.termtypes[type] = obj.Parser;
339   },
340   DefSubr: function (builtin, name, with_var, min_args, max_args)
341   {
342     this.functions[name] = new Xex.Subrountine (builtin, name, with_var,
343                                                 min_args, max_args);
344   },
345   DefSpecial: function (builtin, name, with_var, min_args, max_args)
346   {
347     this.functions[name] = new Xex.SpecialForm (builtin, name, with_var,
348                                                 min_args, max_args);
349   },
350   Defun: function (name, min_args, max_args, args, body)
351   {
352     this.functions[name] =  new Xex.Lambda (name, min_args, max_args,
353                                             args, body);
354   },
355   DefunByFunc: function (func) { this.functions[func.Name] = func; },
356   Defmacro: function (name, min_args, max_args, args, body)
357   {
358     this.functions[name] = new Xex.Macro (name, min_args, max_args,
359                                           args, body);
360   },
361   DefAlias: function (alias, fname)
362   {
363     var func = this.functions[fname];
364
365     if (this.termtypes[alias])
366       throw new Xex.ErrTerm (Xex.Error.FunctionConflict,
367                            "Already defined as a term type: " + alias);
368     if (this.functions[alias])
369       throw new Xex.ErrTerm (Xex.Error.FunctionConflict,
370                            "Already defined as a function: " + alias);
371     if (! func)
372       throw new Xex.ErrTerm (Xex.Error.UnknownFunction, fname);
373     this.functions[alias] = func;
374   },
375   Defvar: function (name)
376   {
377     var vari = this.variables[name];
378     if (vari)
379       {
380         if (vari.Typed)
381           throw new Xex.ErrTerm (Xex.Error.VariableTypeConflict,
382                                "Not a non-typed variable: " + name);
383       }
384     else
385       {
386         vari = new Xex.Variable (this, name, Xex.Zero);
387         this.variables[name] = vari;
388       }
389     return vari;
390   },
391   GetFunc: function (name)
392   {
393     var func = this.functions[name];
394     if (! func)
395       throw new Xex.ErrTerm (Xex.Error.UnknownFunction,
396                              "Unknown function: " + this + ':' + name);
397     return func;
398   },
399   CopyFunc: function (domain, name)
400   {
401     var func = this.functions[name];
402     domain.DefunByFunc (func);
403     return true;
404   },
405   CopyFuncAll: function (domain)
406   {
407     for (var elt in this.functions)
408       domain.DefunByFunc (this.functions[elt]);
409   },
410   GetVarCreate: function (name)
411   {
412     var vari = this.variables[name];
413     if (! vari)
414       vari = this.variables[name] = new Xex.Variable (this, name, Xex.Zero);
415     return vari;
416   },
417   GetVar: function (name) { return this.variables[name]; },
418   SaveValues: function ()
419   {
420     values = {};
421     for (var elt in this.variables)
422       values[elt] = this.variables[elt].val.Clone ();
423     return values;
424   },
425   RestoreValues: function (values)
426   {
427     var name;
428     for (name in values)
429       {
430         var vari = this.variables[name];
431         vari.val = values[name];
432       }
433   }
434 };
435
436 Xex.Term = function (type) { this.type = type; }
437 Xex.Term.prototype = {
438   IsTrue: function () { return true; },
439   Eval: function (domain) { return this.Clone (); },
440   Clone: function (domain) { return this; },
441   Equals: function (obj)
442   {
443     return (this.type == obj.type
444             && this.val
445             && obj.val == this.val);
446   },
447   Matches: function (obj) { return this.Equals (obj); },
448   toString: function ()
449   {
450     if (this.val != undefined)
451       return '<' + this.type + '>' + this.val + '</' + this.type + '>';
452     return '<' + this.type + '/>';
453   }
454 };
455
456 Xex.ParseTerm = function (domain, node)
457 {
458   var name = node.nodeName;
459   var parser = domain.termtypes[name];
460
461   if (parser)
462     return parser (domain, node);
463   if (name == 'defun' || name == 'defmacro')
464     {
465       name = parse_defun_head (domain, node);
466       parse_defun_body (domain, node);
467       return new Xex.StrTerm (name);
468     }
469   if (name == 'defvar')
470     {
471       name = parse_defvar (doamin, node);
472       return new Xex.StrTerm (nanme);
473     }
474
475   return new Xex.Funcall.prototype.Parser (domain, node);
476 }
477
478 Xex.ParseTermList = function (domain, node)
479 {
480   for (var n = node; n; n = n.nextSibling)
481     if (n.nodeType == 1)
482       {
483         if (n.nodeName == 'defun' || n.nodeName == 'defmacro')
484           Xex.parse_defun_head (domain, n);
485       }
486   var terms = new Array ();
487   for (var n = node; n; n = n.nextSibling)
488     if (n.nodeType == 1)
489       {
490         if (n.nodeName == 'defun' || n.nodeName == 'defmacro')
491           Xex.parse_defun_body (domain, n);
492         else if (n.nodeName == 'defvar')
493           Xex.parse_defvar (domain, n);
494         else
495           terms.push (Xex.ParseTerm (domain, n));
496       }
497   return terms;
498 }
499
500 Xex.Varref = function (vname)
501 {
502   this.val = vname;
503 };
504
505 (function () {
506   var proto = new Xex.Term ('varref');
507
508   proto.Clone = function () { return new Xex.Varref (this.val); }
509   proto.Eval = function (domain)
510   {
511     if (! this.vari || this.vari.domain != domain)
512       this.vari = domain.GetVarCreate (this.val);
513     return this.vari.val;
514   }
515
516   proto.Parser = function (domain, node)
517   {
518     return new Xex.Varref (node.attributes['vname'].nodeValue);
519   }
520
521   Xex.Varref.prototype = proto;
522 }) ();
523
524 var null_args = new Array ();
525   
526 Xex.Funcall = function (func, vari, args)
527 {
528   this.func = func;
529   this.vari = vari;
530   this.args = args || null_args;
531 };
532
533 (function () {
534   var proto = new Xex.Term ('funcall');
535
536   proto.Parser = function (domain, node)
537   {
538     var fname = node.nodeName;
539     var attr;
540
541     if (fname == 'funcall')
542       fname = node.attributes['fname']
543     var func = domain.GetFunc (fname);
544     var vari;
545     attr = node.attributes['vname'];
546     vari = attr != undefined ? domain.GetVarCreate (attr.nodeValue) : false;
547     var args = Xex.ParseTermList (domain, node.firstChild);
548     return new Xex.Funcall (func, vari, args);
549   }
550
551   proto.New = function (domain, fname, vname, args)
552   {
553     var func = domain.GetFunc (fname);
554     var vari = vname ? domain.GetVarCreate (vname) : null;
555     var funcall = new Xex.Funcall (func, vari, args);
556     if (func instanceof Xex.Macro)
557       funcall = funcall.Eval (domain);
558     return funcall;
559   }
560
561   proto.Eval = function (domain)
562   {
563     return this.func.Call (domain, this.vari, this.args);
564   }
565
566   proto.Clone = function ()
567   {
568     return new Xex.Funcall (this.func, this.vari, this.args);
569   }
570
571   proto.Equals = function (obj)
572   {
573     return (obj.type == 'funcall'
574             && obj.func == this.func
575             && obj.vari.Equals (this.vari)
576             && obj.args.length == this.func.length);
577   }
578
579   proto.toString = function ()
580   {
581     var arglist = ''
582     var len = this.args.length;
583     if (len == 0)
584       return '<' + this.func.name + '/>';
585     for (var i = 0; i < len; i++)
586       arglist += this.args[i].toString ();
587     return '<' + this.func.name + '>' + arglist + '</' + this.func.name + '>';
588   }
589
590   Xex.Funcall.prototype = proto;
591 }) ();
592
593 Xex.ErrTerm = function (ename, message, stack)
594 {
595   this.ename = ename;
596   this.message = message;
597   this.stack = stack;
598 };
599
600 (function () {
601   var proto = new Xex.Term ('error');
602
603   proto.IsError = true;
604
605   proto.Parser = function (domain, node)
606   {
607     return new Xex.ErrTerm (node.attributes['ename'].nodeValue,
608                             node.innerText, false);
609   }
610
611   proto.CallStack = function () { return stack; }
612
613   proto.SetCallStack = function (value) { statck = value; }
614
615   proto.Clone = function ()
616   {
617     return new Xex.ErrTerm (ename, message, false);
618   }
619
620   proto.Equals = function (obj)
621   {
622     return (obj.IsError
623             && obj.ename == ename && obj.message == message
624             && (obj.stack ? (stack && stack.length == obj.stack.length)
625                 : ! stack));
626   }
627
628   proto.Matches = function (obj)
629   {
630     return (obj.IsError && obj.ename == ename);
631   }
632
633   proto.toString = function ()
634   {
635     return '<error ename="' + this.ename + '">' + this.message + '</error>';
636   }
637
638   Xex.ErrTerm.prototype = proto;
639 }) ();
640
641 Xex.IntTerm = function (num) { this.val = num; };
642 (function () {
643   var proto = new Xex.Term ('integer');
644   proto.IsInt = true;
645   proto.IsTrue = function () { return this.val != 0; }
646   proto.Clone = function () { return new Xex.IntTerm (this.val); }
647   proto.Parser = function (domain, node)
648   {
649     var str = node.firstChild.nodeValue;
650
651     if (str.charAt (0) == '?' && str.length == 2)
652       return new Xex.IntTerm (str.charCodeAt (1));
653     return new Xex.IntTerm (parseInt (node.firstChild.nodeValue));
654   }
655   Xex.IntTerm.prototype = proto;
656 }) ();
657
658 Xex.StrTerm = function (str) { this.val = str; };
659 (function () {
660   var proto = new Xex.Term ('string');
661   proto.IsStr = true;
662   proto.IsTrue = function () { return this.val.length > 0; }
663   proto.Clone = function () { return new Xex.StrTerm (this.val); }
664   proto.Parser = function (domain, node)
665   {
666     return new Xex.StrTerm (node.firstChild.nodeValue);
667   }
668   Xex.StrTerm.prototype = proto;
669 }) ();
670
671 Xex.SymTerm = function (str) { this.val = str; };
672 (function () {
673   var proto = new Xex.Term ('symbol');
674   proto.IsSymbol = true;
675   proto.IsTrue = function () { return this.val != 'nil'; }
676   proto.Clone = function () { return new Xex.SymTerm (this.val); }
677   proto.Parser = function (domain, node)
678   {
679     return new Xex.SymTerm (node.firstChild.nodeValue);
680   }
681   Xex.SymTerm.prototype = proto;
682 }) ();
683
684 Xex.LstTerm = function (list) { this.val = list; };
685 (function () {
686   var proto = new Xex.Term ('list');
687   proto.IsList = true;
688   proto.IsTrue = function () { return this.val.length > 0; }
689   proto.Clone = function () { return new Xex.LstTerm (this.val.slice (0)); }
690
691   proto.Equals = function (obj)
692   {
693     if (obj.type != 'list' || obj.val.length != this.val.length)
694       return false;
695     var i, len = this.val.length;
696     for (i = 0; i < len; i++)
697       if (! this.val[i].Equals (obj.val[i]))
698         return false;
699     return true;
700   }
701
702   proto.Parser = function (domain, node)
703   {
704     var list = Xex.ParseTermList (domain, node.firstChild);
705     return new Xex.LstTerm (list);
706   }
707
708   proto.toString = function ()
709   {
710     var len = this.val.length;
711
712     if (len == 0)
713       return '<list/>';
714     var str = '<list>';
715     for (var i = 0; i < len; i++)
716       str += this.val[i].toString ();
717     return str + '</list>';
718   }
719   Xex.LstTerm.prototype = proto;
720 }) ();
721
722 (function () {
723   var basic = new Xex.Domain ('basic', null, null);
724
725   function Fset (domain, vari, args)
726   {
727     return vari.SetValue (args[0]);
728   }
729
730   function maybe_set_intvar (vari, n)
731   {
732     var term = new Xex.IntTerm (n);
733     if (vari != null)
734       vari.SetValue (term);
735     return term;
736   }
737
738   function Fadd (domain, vari, args)
739   {
740     var n = vari ? vari.val.val : 0;
741     var len = args.length;
742
743     for (var i = 0; i < len; i++)
744       n += args[i].val;
745     return maybe_set_intvar (vari, n);
746   }
747
748   function Fand (domain, vari, args)
749   {
750     var len = args.length;
751     for (var i = 0; i < len; i++)
752     {
753       var result = args[i].Eval (domain);
754       if (domain.Thrown ())
755         return result;
756       if (! result.IsTrue)
757         return Xex.Zero;
758     }
759     return Xex.One;
760   }
761
762   function For (domain, vari, args)
763   {
764     var len = args.length;
765     for (var i = 0; i < len; i++)
766     {
767       var result = args[i].Eval (domain);
768       if (domain.Thrown ())
769         return result;
770       if (result.IsTrue)
771         return Xex.One;
772     }
773     return Xex.Zero;
774   }
775
776   function Fprogn (domain, vari, args)
777   {
778     var result = Xex.One;
779     var len = args.length;
780
781     for (var i = 0; i < len; i++)
782       {
783         result = args[i].Eval (domain);
784         if (domain.Thrown ())
785           return result;
786       }
787     return result;
788   }
789
790   function Fif (domain, vari, args)
791   {
792     var result = args[0].Eval (domain);
793
794     if (domain.Thrown ())
795       return result;
796     if (result.IsTrue)
797       return args[1].Eval (domain);
798     if (args.Length == 2)
799       return Zero;
800     return args[2].Eval (domain);
801   }
802
803   function eval_terms (domain, terms, idx)
804   {
805     var result = Xex.Zero;
806     domain.caught = false;
807     for (var i = idx; i < terms.length; i++)
808       {
809         result = terms[i].Eval (domain);
810         if (domain.Thrown ())
811           return result;
812       }
813     return result;
814   }
815
816   function Fcatch (domain, vari, args)
817   {
818     var caught = false;
819     var result;
820
821     if (args[0].IsError)
822       {
823         try {
824           result = eval_terms (domain, args, 1);
825         } catch (e) {
826           if (e instanceof Xex.ErrTerm)
827             {
828               if (! args[0].Matches (e))
829                 throw e;
830               if (vari)
831                 vari.SetValue (e);
832               return Xex.One;
833             }
834         }
835       }
836     else if (args[0].IsSymbol)
837       {
838         try {
839           domain.Catch (args[0].val);
840           result = eval_terms (domain, args, 1);
841           if (domain.caught)
842             {
843               if (vari != null)
844                 vari.SetValue (result);
845               return Xex.One;
846             }
847           return Xex.Zero;
848         } finally {
849           domain.Uncatch ();
850         }
851       }
852     throw new Xex.ErrTerm (Xex.Error.WrongArgument,
853                            "Not a symbol nor an error: " + args[0]);
854   }
855
856   function Fthrow (domain, vari, args)
857   {
858     if (args[0].IsSymbl)
859       {
860         domain.ThrowSymbol (args[0]);
861         return (args[args.length - 1]);
862       }
863     if (args[0].IsError)
864       {
865         throw args[0];
866       }
867     throw new Xex.ErrTerm (Xex.Error.WrongArgument,
868                            "Not a symbol nor an error:" + args[0]);
869   }
870
871   Xex.BasicDomain = basic;
872
873   basic.DefSubr (Fset, "set", true, 1, 1);
874   basic.DefSubr (Fadd, "add", true, 1, -1);
875   basic.DefSubr (Fthrow, "throw", false, 1, 2);
876
877   basic.DefSpecial (Fand, "and", false, 1, -1);
878   basic.DefSpecial (For, "or", false, 1, -1);
879   basic.DefAlias ("=", "set");
880   basic.DefSpecial (Fprogn, "progn", false, 1, -1);
881   basic.DefSpecial (Fif, "if", false, 2, 3);
882   basic.DefSpecial (Fcatch, "catch", true, 2, -1);
883
884   basic.DefType (Xex.Funcall.prototype);
885   basic.DefType (Xex.Varref.prototype);
886   basic.DefType (Xex.ErrTerm.prototype);
887   basic.DefType (Xex.IntTerm.prototype);
888   basic.DefType (Xex.StrTerm.prototype);
889   basic.DefType (Xex.SymTerm.prototype);
890   basic.DefType (Xex.LstTerm.prototype);
891
892 }) ();
893
894 Xex.Zero = new Xex.IntTerm (0);
895 Xex.One = new Xex.IntTerm (1);
896
897 Xex.Load = function (server, file)
898 {
899   var obj = new XMLHttpRequest ();
900   var url = server ? server + '/' + file : file;
901   obj.open ('GET', url, false);
902   obj.overrideMimeType ('text/xml');
903   obj.send ('');
904   return obj.responseXML.firstChild;
905 }
906
907 var MIM = {
908   // URL of the input method server.
909   server: "http://www.m17n.org/common/mim-js",
910   // Boolean flag to tell if MIM is active or not.
911   enabled: true,
912   // Boolean flag to tell if MIM is running in debug mode or not.
913   debug: false,
914   // List of registered input methods.
915   im_list: {},
916   // Global input method data
917   im_global: null,
918   // Currently selected input method.
919   current: false,
920
921   // enum
922   LoadStatus: { NotLoaded:0, Loading:1, Loaded:2, Error:-1 },
923   ChangedStatus: {
924     None:       0x00,
925     StateTitle: 0x01,
926     PreeditText:0x02,
927     CursorPos:  0x04,
928     CandidateList:0x08,
929     CandidateIndex:0x10,
930     CandidateShow:0x20,
931     Preedit:    0x06,           // PreeditText | CursorPos
932     Candidate:  0x38 // CandidateList | CandidateIndex | CandidateShow
933   },
934   KeyModifier: {
935     SL: 0x00400000,
936     SR: 0x00800000,
937     S:  0x00C00000,
938     CL: 0x01000000,
939     CR: 0x02000000,
940     C:  0x03000000,
941     AL: 0x04000000,
942     AR: 0x08000000,
943     A:  0x0C000000,
944     ML: 0x04000000,
945     MR: 0x08000000,
946     M:  0x0C000000,
947     G:  0x10000000,
948     s:  0x20000000,
949     H:  0x40000000,
950     High:       0x70000000,
951     All:        0x7FC00000
952   },
953   Error: {
954     ParseError: "parse-error"
955   }
956 };
957   
958 (function () {
959   var keysyms = new Array ();
960   keysyms["bs"] = "backspace";
961   keysyms["lf"] = "linefeed";
962   keysyms["cr"] = keysyms["enter"] = "return";
963   keysyms["esc"] = "escape";
964   keysyms["spc"] = "space";
965   keysyms["del"] = "delete";
966
967   function decode_keysym (str) {
968     var parts = str.split ("-");
969     var len = parts.length, i;
970     var has_modifier = len > 1;
971
972     for (i = 0; i < len - 1; i++)
973       if (! MIM.KeyModifier.hasOwnProperty (parts[i]))
974         return false;
975     var key = parts[len - 1];
976     if (key.length > 1)
977       {
978         key = keysyms[key.toLowerCase ()];
979         if (key)
980           {
981             if (len > 1)
982               {
983                 str = parts[0];
984                 for (i = 1; i < len - 1; i++)
985                   str += '-' + parts[i];
986                 str += '-' + key;
987               }
988             else
989               str = key;
990           }
991       }
992     if (has_modifier)
993       {
994         parts = new Array ();
995         parts.push (str);
996         return parts;
997       }
998     return str;
999   }
1000
1001   MIM.Key = function (val)
1002   {
1003     this.key;
1004     this.has_modifier = false;
1005     if (typeof val == 'string' || val instanceof String)
1006       {
1007         this.key = decode_keysym (val);
1008         if (! this.key)
1009           throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + val);
1010         if (this.key instanceof Array)
1011           {
1012             this.key = this.key[0];
1013             this.has_modifier = true;
1014           }
1015       }
1016     else if (typeof val == 'number' || val instanceof Number)
1017       this.key = String.fromCharCode (val);
1018     else
1019       throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + val);
1020   }
1021
1022   MIM.Key.prototype.toString = function () { return this.key; };
1023 }) ();
1024
1025 (function () {
1026   MIM.KeySeq = function (seq)
1027   {
1028     this.val = new Array ();
1029     this.has_modifier = false;
1030
1031     if (seq)
1032       {
1033         if (seq.IsList)
1034           {
1035             var len = seq.val.length;
1036             for (var i = 0; i < len; i++)
1037               {
1038                 var v = seq.val[i];
1039                 if (v.type != 'string' && v.type != 'integer'
1040                     && v.type != 'symbol')
1041                   throw new Xex.ErrTerm (MIM.Error.ParseError,
1042                                          "Invalid key: " + v);
1043                 var key = new MIM.Key (v.val);
1044                 this.val.push (key);
1045                 if (key.has_modifier)
1046                   this.has_modifier = true;
1047               }
1048           }
1049         else if (seq.IsStr)
1050           {
1051             var len = seq.val.length;
1052             for (var i = 0; i < len; i++)
1053               this.val.push (new MIM.Key (seq.val.charCodeAt (i)));
1054           }
1055         else
1056           throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + seq);
1057       }
1058   }
1059
1060   var proto = new Xex.Term ('keyseq');
1061   proto.Clone = function () { return this; }
1062   proto.Parser = function (domain, node)
1063   {
1064     var seq = new Array ();
1065     for (node = node.firstChild; node; node = node.nextSibling)
1066       if (node.nodeType == 1)
1067         {
1068           var term = Xex.ParseTerm (domain, node);
1069           return new MIM.KeySeq (term);
1070         }
1071     throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid keyseq");
1072   }
1073   proto.toString = function ()
1074   {
1075     var len = this.val.length;
1076     if (len == 0)
1077       return '<keyseq/>';
1078     var first = true;
1079     var str = '<keyseq>';
1080     for (var i = 0; i < len; i++)
1081       {
1082         if (first)
1083           first = false;
1084         else if (this.has_modifier)
1085           str += ' ';
1086         str += this.val[i].toString ();
1087       }
1088     return str + '</keyseq>';
1089   }
1090
1091   MIM.KeySeq.prototype = proto;
1092 }) ();
1093
1094 (function () {
1095   MIM.Marker = function () { }
1096   MIM.Marker.prototype = new Xex.Term ('marker');
1097   MIM.Marker.prototype.CharAt = function (ic)
1098   {
1099     var p = this.Position (ic);
1100     if (p < 0)
1101       return ic.GetSurroundingChar (p);
1102     else if (pos >= ic.preedit.length)
1103       return ic.GetSurroundingChar (p - ic.preedit.length);
1104     return ic.preedit.charCodeAt (p);
1105   }
1106
1107   MIM.NamedMarker = function (name) { this.val = name; }
1108   MIM.NamedMarker.prototype = new MIM.Marker ();
1109   MIM.NamedMarker.prototype.Position = function (ic)
1110   {
1111     var p = ic.marker_positions[this.val];
1112     return (p == undefined ? 0 : p);
1113   }
1114   MIM.NamedMarker.prototype.Mark = function (ic)
1115   {
1116     ic.marker_positions[this.val] = ic.cursor_pos;
1117   }
1118
1119   MIM.PredefinedMarker = function (name) { this.val = name; }
1120   MIM.PredefinedMarker.prototype = new MIM.Marker ();
1121   MIM.PredefinedMarker.prototype.Position = function (ic)
1122   {
1123     if (typeof this.pos == 'number')
1124       return this.pos;
1125     return this.pos (ic);
1126   }
1127
1128   var predefined = { }
1129
1130   function def_predefined (name, position)
1131   {
1132     predefined[name] = new MIM.PredefinedMarker (name);
1133     predefined[name].pos = position;
1134   }
1135
1136   def_predefined ('@<', 0);
1137   def_predefined ('@>', function (ic) { return ic.preedit.length; });
1138   def_predefined ('@-', function (ic) { return ic.cursor_pos - 1; });
1139   def_predefined ('@+', function (ic) { return ic.cursor_pos + 1; });
1140   def_predefined ('@[', function (ic) {
1141     if (ic.cursor_pos > 0)
1142       {
1143         var pos = ic.cursor_pos;
1144         return ic.preedit.FindProp ('candidates', pos - 1).from;
1145       }
1146     return 0;
1147   });
1148   def_predefined ('@]', function (ic) {
1149     if (ic.cursor_pos < ic.preedit.length - 1)
1150       {
1151         var pos = ic.cursor_pos;
1152         return ic.preedit.FindProp ('candidates', pos).to;
1153       }
1154     return ic.preedit.length;
1155   });
1156   for (var i = 0; i < 10; i++)
1157     def_predefined ("@" + i, i);
1158   predefined['@first'] = predefined['@<'];
1159   predefined['@last'] = predefined['@>'];
1160   predefined['@previous'] = predefined['@-'];
1161   predefined['@next'] = predefined['@+'];
1162   predefined['@previous-candidate-change'] = predefined['@['];
1163   predefined['@next-candidate-change'] = predefined['@]'];
1164
1165   MIM.SurroundMarker = function (name)
1166   {
1167     this.val = name;
1168     this.distance = parseInt (name.slice (2));
1169     if (isNaN (this.distance))
1170       throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid marker: " + name);
1171   }
1172   MIM.SurroundMarker.prototype = new MIM.Marker ();
1173   MIM.SurroundMarker.prototype.Position = function (ic)
1174   {
1175     return ic.cursor_pos + this.distance;
1176   }
1177
1178   MIM.Marker.prototype.Parser = function (domain, node)
1179   {
1180     var name = node.firstChild.nodeValue;
1181     if (name.charAt (0) == '@')
1182       {
1183         var n = predefined[name];
1184         if (n)
1185           return n;
1186         if (name.charAt (1) == '-')
1187           return new MIM.SurroundMarker (name);
1188         throw new Xex.ErrTerm (MIM.Error.ParseError,
1189                                "Invalid marker: " + name);
1190       }
1191     return new MIM.NamedMarker (name);
1192   }
1193 }) ();
1194
1195 MIM.Selector = function (name)
1196 {
1197   this.val = name;
1198 }
1199 MIM.Selector.prototype = new Xex.Term ('selector');
1200 (function () {
1201   var selectors = {};
1202   selectors["@<"] = selectors["@first"] = new MIM.Selector ('@<');
1203   selectors["@="] = selectors["@current"] = new MIM.Selector ('@=');
1204   selectors["@>"] = selectors["@last"] = new MIM.Selector ('@>');
1205   selectors["@-"] = selectors["@previous"] = new MIM.Selector ('@-');
1206   selectors["@+"] = selectors["@next"] = new MIM.Selector ('@+');
1207   selectors["@["] = selectors["@previous-candidate-change"]
1208     = new MIM.Selector ('@[');
1209   selectors["@]"] = selectors["@next-candidate-change"]
1210     = new MIM.Selector ('@]');
1211
1212   MIM.Selector.prototype.Parser = function (domain, node)
1213   {
1214     var name = node.firstChild.nodeValue;
1215     var s = selectors[name];
1216     if (! s)
1217       throw new Xex.ErrTerm (MIM.Error.ParseError,
1218                              "Invalid selector: " + name);
1219     return s;
1220   }
1221 }) ();
1222
1223 MIM.Rule = function (keyseq, actions)
1224 {
1225   this.keyseq = keyseq;
1226   this.actions = actions;
1227 }
1228 MIM.Rule.prototype = new Xex.Term ('rule');
1229 MIM.Rule.prototype.Parser = function (domain, node)
1230 {
1231   var n;
1232   for (n = node.firstChild; n && n.nodeType != 1; n = n.nextSibling);
1233   if (! n)
1234     throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid rule:" + node);
1235   var keyseq = Xex.ParseTerm (domain, n);
1236   if (keyseq.type != 'keyseq')
1237     throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid rule:" + node);
1238   var actions = Xex.ParseTermList (domain, n.nextSibling);
1239   return new MIM.Rule (keyseq, actions);
1240 }
1241 MIM.Rule.prototype.toString = function ()
1242 {
1243   return '<rule/>';
1244 }
1245
1246 MIM.Map = function (name)
1247 {
1248   this.name = name;
1249   this.rules = new Array ();
1250 };
1251 (function () {
1252   var proto = new Xex.Term ('map');
1253
1254   proto.Parser = function (domain, node)
1255   {
1256     var name = node.attributes['mname'].nodeValue;
1257     if (! name)
1258       throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid map");
1259     var map = new MIM.Map (name);
1260     for (var n = node.firstChild; n; n = n.nextSibling)
1261       if (n.nodeType == 1)
1262         map.rules.push (Xex.ParseTerm (domain, n));
1263     return map;
1264   }
1265
1266   proto.toString = function ()
1267   {
1268     var str = '<map mname="' + this.name + '">';
1269     var len = this.rules.length;
1270     for (i = 0; i < len; i++)
1271       str += this.rules[i];
1272     return str + '</map>';
1273   }
1274
1275   MIM.Map.prototype = proto;
1276 }) ();
1277
1278 Xex.CatchTag._mimtag = new Xex.SymTerm ('@mimtag');
1279
1280 MIM.Action = function (domain, terms)
1281 {
1282   var args = new Array ();
1283   args.push (Xex.CatchTag_.mimtag);
1284   for (var i = 0; i < terms.length; i++)
1285     args.push (terms[i]);
1286   this.action = Xex.Funcall.prototype.New (domain, 'catch', null, args);
1287 }
1288
1289 MIM.Action.prototype.Run = function (domain)
1290 {
1291   var result = this.action.Eval (domain);
1292   if (result.type == 'error')
1293     {
1294       domain.context.Error = result.toString ();
1295       return false;
1296     }
1297   return (result != Xex.CatchTag._mimtag);
1298 }
1299
1300 MIM.Keymap = function ()
1301 {
1302   this.submaps = null;
1303   this.actions = null;
1304 };
1305 (function () {
1306   var proto = {};
1307
1308   function add_rule (keymap, rule)
1309   {
1310     var keyseq = rule.keyseq;
1311     var len = keyseq.val.length;
1312
1313     for (var i = 0; i < len; i++)
1314       {
1315         var key = keyseq.val[i];
1316         var sub = false;
1317
1318         if (! keymap.submaps)
1319           keymap.submaps = {};
1320         else
1321           sub = keymap.submaps[key.key];
1322         if (! sub)
1323           keymap.submaps[key.key] = sub = new MIM.Keymap ();
1324         keymap = sub;
1325       }
1326     keymap.actions = rule.actions;
1327   }
1328
1329   proto.Add = function (map)
1330   {
1331     var rules = map.rules;
1332     var len = rules.length;
1333
1334     for (var i = 0; i < len; i++)
1335       add_rule (this, rules[i]);
1336   }
1337   proto.Lookup = function (keys, index)
1338   {
1339     var sub;
1340
1341     if (index < keys.val.length && this.submaps
1342         && (sub = this.submaps[keys.val[index].key]))
1343       {
1344         index++;
1345         return sub.Lookup (keys, index);
1346       }
1347     return { map: this, index: index };
1348   }
1349
1350   MIM.Keymap.prototype = proto;
1351 }) ();
1352
1353 MIM.State = function (name)
1354 {
1355   this.name = name;
1356   this.keymap = new MIM.Keymap ();
1357 };
1358 (function () {
1359   var proto = new Xex.Term ('state');
1360
1361   proto.Parser = function (domain, node)
1362   {
1363     var map_list = domain.map_list;
1364     var name = node.attributes['sname'].nodeValue;
1365     if (! name)
1366       throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid map");
1367     var state = new MIM.State (name);
1368     for (node = node.firstChild; node; node = node.nextSibling)
1369       {
1370         if (node.nodeType != 1)
1371           continue;
1372         if (node.nodeName == 'branch')
1373           {
1374             state.keymap.Add (map_list[node.attributes['mname'].nodeValue]);
1375             state.keymap.actions = Xex.ParseTermList (domain, node.firstChild);
1376           }
1377         else if (node.nodeName == 'state-hook')
1378           state.enter_actions = Xex.ParseTermList (domain, node.firstChild);
1379         else if (node.nodeName == 'catch-all-branch')
1380           state.fallback_actions = Xex.ParseTermList (domain, node.firstChild);
1381         else if (node.nodeName == 'title')
1382           state.title = node.firstChild.nodeValue;
1383       }
1384     return state;
1385   }
1386
1387   proto.toString = function ()
1388   {
1389     return '<state sname="' + this.name + '">' + this.keymap + '</state>';
1390   }
1391
1392   MIM.State.prototype = proto;
1393 }) ();
1394
1395 MIM.im_domain = new Xex.Domain ('input-method', null, null);
1396 MIM.im_domain.DefType (MIM.KeySeq.prototype);
1397 MIM.im_domain.DefType (MIM.Marker.prototype);
1398 MIM.im_domain.DefType (MIM.Selector.prototype);
1399 MIM.im_domain.DefType (MIM.Rule.prototype);
1400 MIM.im_domain.DefType (MIM.Map.prototype);
1401 MIM.im_domain.DefType (MIM.State.prototype);
1402
1403 (function () {
1404   var im_domain = MIM.im_domain;
1405
1406   function Finsert (domain, vari, args)
1407   {
1408     domain.context.insert (args[0].val, null);
1409   }
1410
1411   im_domain.DefSubr (Finsert, "insert", false, 1, 1);
1412 }) ();
1413
1414 (function () {
1415   var parsers = { };
1416   parsers['description'] = function (node)
1417   {
1418     this.description = node.firstChild.nodeValue;
1419   }
1420   parsers['title'] = function (node)
1421   {
1422     this.title = node.firstChild.nodeValue;
1423   }
1424   parsers['map-list'] = function (node)
1425   {
1426     for (node = node.firstChild; node; node = node.nextSibling)
1427       {
1428         if (node.nodeType != 1 || node.nodeName != 'map')
1429           continue;
1430         var map = Xex.ParseTerm (this.domain, node);
1431         this.map_list[map.name] = map;
1432       }
1433   }
1434   parsers['state-list'] = function (node)
1435   {
1436     this.domain.map_list = this.map_list;
1437     for (node = node.firstChild; node; node = node.nextSibling)
1438       {
1439         if (node.nodeType != 1 || node.nodeName != 'state')
1440           continue;
1441         var state = Xex.ParseTerm (this.domain, node);
1442         if (! state.title)
1443           state.title = this.title;
1444         if (! this.initial_state)
1445           this.initial_state = state;
1446         this.state_list[state.name] = state;
1447       }
1448     delete this.domain.map_list;
1449   }
1450
1451   MIM.IM = function (lang, name, extra_id, file)
1452   {
1453     this.lang = lang;
1454     this.name = name;
1455     this.extra_id = extra_id;
1456     this.file = file;
1457     this.load_status = MIM.LoadStatus.NotLoaded;
1458     this.domain = new Xex.Domain (this.lang + '-' + this.name,
1459                                   MIM.im_domain, null);
1460   }
1461
1462   var proto = {
1463     Load: function ()
1464     {
1465       var node = Xex.Load (null, this.file);
1466       if (! node)
1467         {
1468           this.load_status = MIM.LoadStatus.Error;
1469           return false;
1470         }
1471       this.map_list = {};
1472       this.initial_state = null;
1473       this.state_list = {};
1474       for (node = node.firstChild; node; node = node.nextSibling)
1475         {
1476           if (node.nodeType != 1)
1477             continue;
1478           var name = node.nodeName;
1479           var parser = parsers[name];
1480           if (parser)
1481             parser.call (this, node);
1482         }
1483       this.load_status = MIM.LoadStatus.Loaded;
1484       return true;
1485     }
1486   }
1487
1488   MIM.IM.prototype = proto;
1489
1490   MIM.IC = function (im)
1491   {
1492     if (im.load_status == MIM.LoadStatus.NotLoaded)
1493       im.Load ();
1494     if (im.load_status != MIM.LoadStatus.Loaded)
1495       alert ('im:' + im.name + ' error:' + im.load_status);
1496     this.im = im;
1497     this.domain = new Xex.Domain ('context', im.domain, this);
1498     this.active = true;
1499     this.reset ();
1500     this.spot = 0;
1501   }
1502
1503   MIM.CandidateTable = function ()
1504   {
1505     this.table = new Array ();
1506   }
1507
1508   MIM.CandidateTable.prototype.get = function (from)
1509   {
1510     for (var i = 0; i < this.table.length; i++)
1511       {
1512         var elt = this.table[i];
1513         if (elt.from <= from && elt.to > from)
1514           return elt.val;
1515       }
1516   }
1517
1518   MIM.CandidateTable.prototype.put = function (from, to, candidates)
1519   {
1520     for (var i = 0; i < this.table.length; i++)
1521       {
1522         var elt = this.table[i];
1523         if (elt.from >= from && elt.from < to
1524             || elt.to >= from && elt.to < to)
1525           {
1526             elt.from = from;
1527             elt.to = to;
1528             elt.val = candidates;
1529             return;
1530           }
1531       }
1532     this.table.push ({ from: from, to: to, val: candidates });
1533   }
1534
1535   MIM.CandidateTable.prototype.adjust = function (from, to, inserted)
1536   {
1537     var diff = inserted - (to - from);
1538     for (var i = 0; i < this.table.length; i++)
1539       {
1540         var elt = this.table[i];
1541         if (elt.from >= to)
1542           {
1543             elt.from += diff;
1544             elt.to += diff;
1545           }
1546       }
1547   }
1548
1549   MIM.CandidateTable.prototype.clear = function ()
1550   {
1551     this.table.length = 0;
1552   }
1553
1554   function set_cursor (prefix, pos)
1555   {
1556     this.cursor_pos = pos;
1557     if (pos > 0)
1558       this.candidates = this.candidate_table.get (pos - 1);
1559     else
1560       this.candidates = null;
1561   }
1562
1563   function save_state ()
1564   {
1565     this.state_var_values = this.domain.SaveValues ();
1566     this.state_preedit = this.preedit;
1567     this.state_key_head = this.key_head;
1568     this.state_pos = this.cursor_pos;
1569   }
1570
1571   function restore_state ()
1572   {
1573     this.domain.RestoreValues (this.state_var_values);
1574     this.preedit = this.state_preedit;
1575     set_cursor.call (this, "restore", this.state_pos);
1576   }
1577
1578   function handle_key ()
1579   {
1580     var out = this.keymap.Lookup (this.keys, this.key_head);
1581     var sub = out.map;
1582     var branch_actions = this.state.keymap.actions;
1583
1584     MIM.log ('handling ' + this.keys.val[this.key_head]
1585              + ' in ' + this.state.name);
1586     this.key_head = out.index;
1587     if (sub != this.keymap)
1588       {
1589
1590         restore_state.call (this);
1591         this.keymap = sub;
1592         MIM.log ('submap found');
1593         if (this.keymap.actions != null)
1594           {
1595             MIM.log ('taking map actions:');
1596             if (! this.take_actions (this.keymap.actions))
1597               return false;
1598           }
1599         else if (this.keymap.submaps != null)
1600           {
1601             MIM.log ('no map actions, inserting key:');
1602             for (var i = this.state_key_head; i < this.key_head; i++)
1603               this.preedit_replace (this.cursor_pos, this.cursor_pos,
1604                                     this.keys.val[i].key, null);
1605           }
1606         if (this.keymap.submaps == null)
1607           {
1608             MIM.log ('terminal:');
1609             if (this.keymap.branch_actions != null)
1610               {
1611                 MIM.log ('branch actions:');
1612                 if (! this.take_actions (branch_actions))
1613                   return false;
1614               }
1615             if (this.keymap != this.state.keymap)
1616               this.shift (this.state);
1617           }
1618       }
1619     else
1620       {
1621         MIM.log ("no submap");
1622         var current_state = this.state;
1623
1624         if (branch_actions)
1625           {
1626             MIM.log ("branch actions");
1627             if (! this.take_actions (this.keymap.branch_actions))
1628               return false;
1629           }
1630         if (this.state == current_state)
1631           {
1632             if (this.state == this.initial_state
1633                 && this.key_head < this.keys.val.length)
1634               return false;
1635             if (this.keymap != this.state.keymap)
1636               this.shift (this.state);
1637             else if (this.keymap.branch_actions == null)
1638               this.shift (this.initial_state);
1639           }
1640       }
1641     return true;
1642   }
1643
1644   proto = {
1645     init: function ()
1646     {
1647       this.produced = null;
1648       this.preedit = '';
1649       this.cursor_pos = 0;
1650       this.marker_positions = {};
1651       this.candidates = null;
1652       this.candidate_show = false;
1653       this.state = null;
1654       this.prev_state = null;
1655       this.initial_state = this.im.initial_state;
1656       this.title = this.initial_state.title;
1657       this.state_preedit = '';
1658       this.state_key_head = 0;
1659       this.state_var_values = {};
1660       this.state_pos = 0;
1661       this.keymap = null;
1662       this.keys = new MIM.KeySeq ();
1663       this.key_head = 0;
1664       this.key_unhandled = false;
1665       this.unhandled_key = null;
1666       this.changed = MIM.ChangedStatus.None;
1667       this.error_message = '';
1668       this.title = this.initial_state.title;
1669       this.produced = null;
1670       this.preedit = '';
1671       this.marker_positions = {};
1672       this.candidate_table = new MIM.CandidateTable ();
1673       this.candidates = null;
1674       this.candidate_show = false;
1675     },
1676
1677     reset: function ()
1678     {
1679       this.init ();
1680       this.state_var_values = {};
1681       this.shift (this.initial_state);
1682     },
1683
1684     catch_args: new Array (Xex.CatchTag._mimtag, null),
1685
1686     take_actions: function (actions)
1687     {
1688       var func_progn = this.domain.GetFunc ('progn');
1689       var func_catch = this.domain.GetFunc ('catch');
1690       this.catch_args[1] = new Xex.Funcall (func_progn, null, actions);
1691       var term = new Xex.Funcall (func_catch, null, this.catch_args);
1692       term = term.Eval (this.domain);
1693       return (! term.IsSymbol || term.val != '@mimtag');
1694     },
1695
1696     GetSurroundingChar: function (pos)
1697     {
1698       if (pos < 0 ? this.caret_pos < - pos : this.target.value.length < pos)
1699         return 0;
1700       return this.target.value.charCodeAt (this.caret_pos + pos);
1701     },
1702     
1703     adjust_markers: function (from, to, inserted)
1704     {
1705       var diff = inserted - (to - from);
1706
1707       for (var m in this.marker_positions)
1708         if (this.marker_positions[m] > from)
1709           this.marker_positions[m] = (this.marker_positions[m] >= to
1710                                       ? pos + diff : from);
1711       if (this.cursor_pos >= to)
1712         set_cursor.call (this, 'adjust', this.cursor_pos + diff);
1713       else if (this.cursor_pos > from)
1714         set_cursor.call (this, 'adjust', from)
1715     },
1716
1717     preedit_replace: function (from, to, text, candidates)
1718     {
1719       this.preedit = (this.preedit.substring (0, from)
1720                       + text + this.preedit.substring (to));
1721       this.adjust_markers (from, to, text.length);
1722       this.candidate_table.adjust (from, to, text.length);
1723       if (candidates)
1724         this.candidate_table.put (from, from + text.length, candidates)
1725     },
1726
1727     insert: function (text, candidates)
1728     {
1729       this.preedit_replace (this.cursor_pos, this.cursor_pos, text, candidates);
1730       this.changed = MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos;
1731     },
1732
1733     del: function (pos)
1734     {
1735       if (pos < 0)
1736         {
1737           this.DelSurroundText (pos);
1738           pos = 0;
1739         }
1740       else if (pos > this.preedit.length)
1741         {
1742           this.DelSurroundText (pos - this.preedit.length);
1743           pos = this.preedit.length;
1744         }
1745       if  (pos < this.cursor_pos)
1746         this.preedit = (this.predit.substring (0, pos)
1747                         + this.preedit.substring (this.cursor_pos));
1748       else
1749         this.preedit = (this.preedit.substring (0, this.cursor_pos)
1750                         + this.predit.substring (pos));
1751     },
1752
1753     show: function ()
1754     {
1755       this.candidate_show = true;
1756       this.changed |= MIM.ChangedStatus.CandidateShow;
1757     },
1758
1759     hide: function ()
1760     {
1761       this.candidate_show = false;
1762       this.changed |= MIM.ChangedStatus.CandidateShow;
1763     },
1764
1765     move: function (pos)
1766     {
1767       if (pos < 0)
1768         pos = 0;
1769       else if (pos > this.preedit.length)
1770         pos = this.preedit.length;
1771       if (pos != this.cursor_pos)
1772         {
1773           set_cursor.call (this, 'move', pos);
1774           this.changed |= MIM.ChangedStatus.Preedit;
1775         }
1776     },
1777
1778     pushback: function (n)
1779     {
1780       if (n instanceof MIM.KeySeq)
1781         {
1782           if (this.key_head > 0)
1783             this.key_head--;
1784           if (this.key_head < this.keys.val.length)
1785             this.keys.val.splice (this.key_head,
1786                                   this.keys.val.length - this.key_head);
1787           for (var i = 0; i < n.val.length; i++)
1788             this.keys.val.push (n.val[i]);
1789           return;
1790         }
1791       if (n > 0)
1792         {
1793           this.key_head -= n;
1794           if (this.key_head < 0)
1795             this.key_head = 0;
1796         }
1797       else if (n == 0)
1798         this.key_head = 0;
1799       else
1800       {
1801         this.key_head = - n;
1802         if (this.key_head > this.keys.val.length)
1803           this.key_head = this.keys.val.length;
1804       }
1805     },
1806
1807     pop: function ()
1808     {
1809       if (this.key_head < this.keys.val.length)
1810         this.keys.val.splice (this.key_head, 1);
1811     },
1812
1813     commit: function ()
1814     {
1815       if (this.preedit.length > 0)
1816       {
1817         this.candidate_table.clear ();
1818         this.produced += this.preedit;
1819         this.preedit_replace.call (this, 0, this.preedit.Length, '', null);
1820       }
1821     },
1822
1823     shift: function (state)
1824     {
1825       if (state == null)
1826       {
1827         MIM.log ("shifting back to previous");
1828         if (this.prev_state == null)
1829           return;
1830         state = this.prev_state;
1831       }
1832       else
1833         MIM.log ("shifting to " + state.name);
1834
1835       if (state == this.initial_state)
1836         {
1837           this.commit ();
1838           this.keys.val.splice (0, this.key_head);
1839           this.key_head = 0;
1840           if (state != this.state)
1841             {
1842               this.domain.RestoreValues (this.state_initial_var_values);
1843               if (state.enter_actions != null)
1844                 take_actions.call (state.enter_actions);
1845             }
1846           this.prev_state = null;
1847         }
1848       else
1849         {
1850           if (state != this.state && state.enter_actions != null)
1851             take_actions.call (state.enter_actions);
1852           this.prev_state = this.state;
1853         }
1854       save_state.call (this);
1855       if (! this.state || this.state.title != state.title)
1856         this.changed |= MIM.ChangedStatus.StateTitle;
1857       this.state = state;
1858       this.keymap = state.keymap;
1859     },
1860
1861     Filter: function (key)
1862     {
1863       if (! this.active)
1864         {
1865           this.key_unhandled = true;
1866           this.unhandled_key = key;
1867           return false;
1868         }
1869       if (key.key == '_reload')
1870         return true;
1871       this.changed = MIM.ChangedStatus.None;
1872       this.produced = '';
1873       this.key_unhandled = false;
1874       this.keys.val.push (key);
1875       var count = 0;
1876       while (this.key_head < this.keys.val.length)
1877         {
1878           if (! handle_key.call (this))
1879             {
1880               this.unhandled_key = this.keys.val[this.key_head++];
1881               this.key_unhandled = true;
1882               break;
1883             }
1884           if (++count == 10)
1885             break;
1886         }
1887       this.keys.val.splice (0, this.key_head);
1888       this.key_head = 0;
1889       return (! this.key_unhandled && this.produced.length == 0);
1890     }
1891   }
1892
1893   MIM.IC.prototype = proto;
1894
1895   var node = Xex.Load (null, "imlist.xml");
1896   for (node = node.firstChild; node; node = node.nextSibling)
1897     if (node.nodeName == 'input-method')
1898       {
1899         var lang, name, extra_id, file;
1900
1901         for (var n = node.firstChild; n; n = n.nextSibling)
1902           {
1903             if (n.nodeName == 'language')
1904               lang = n.firstChild.nodeValue;
1905             else if (n.nodeName == 'name')
1906               name = n.firstChild.nodeValue;
1907             else if (n.nodeName == 'extra-id')
1908               extra_id = n.firstChild.nodeValue;
1909             else if (n.nodeName == 'filename')
1910               file = n.firstChild.nodeValue;
1911           }
1912         if (! MIM.im_list[lang])
1913           MIM.im_list[lang] = {};
1914         MIM.im_list[lang][name] = new MIM.IM (lang, name, extra_id, file);
1915       }
1916   node = undefined;
1917 }) ();
1918
1919 (function () {
1920   var keys = new Array ();
1921   keys[0x09] = 'tab';
1922   keys[0x08] = 'backspace';
1923   keys[0x0D] = 'return';
1924   keys[0x1B] = 'escape';
1925   keys[0x20] = 'space';
1926   keys[0x21] = 'pageup';
1927   keys[0x22] = 'pagedown';
1928   keys[0x23] = 'end';
1929   keys[0x24] = 'home';
1930   keys[0x25] = 'left';
1931   keys[0x26] = 'up';
1932   keys[0x27] = 'right';
1933   keys[0x28] = 'down';
1934   keys[0x2D] = 'insert';
1935   keys[0x2E] = 'delete';
1936   for (var i = 1; i <= 12; i++)
1937     keys[111 + i] = "f" + i;
1938   keys[0x90] = "numlock";
1939   keys[0xF0] = "capslock";
1940
1941   MIM.decode_key_event = function (event)
1942   {
1943     var key = ((event.type == 'keydown' || event.keyCode) ? event.keyCode
1944                : event.charCode ? event.charCode
1945                : false);
1946     if (! key)
1947       return false;
1948     if (event.type == 'keydown')
1949       {
1950         key = keys[key];
1951         if (! key)
1952           return false;
1953         if (event.shiftKey) key = "S-" + key ;
1954       }
1955     else
1956       key = String.fromCharCode (key);
1957     if (event.altKey) key = "A-" + key ;
1958     if (event.ctrlKey) key = "C-" + key ;
1959     return new MIM.Key (key);
1960   }
1961 }) ();
1962
1963 MIM.add_event_listener
1964   = (window.addEventListener
1965      ? function (target, type, listener) {
1966        target.addEventListener (type, listener, false);
1967      }
1968      : window.attachEvent
1969      ? function (target, type, listener) {
1970        target.attachEvent ('on' + type,
1971                            function() {
1972                              listener.call (target, window.event);
1973                            });
1974      }
1975      : function (target, type, listener) {
1976        target['on' + type]
1977          = function (e) { listener.call (target, e || window.event); };
1978      });
1979
1980 MIM.log = function (msg)
1981 {
1982   var node = document.getElementById ('log');
1983   node.value += msg + "\n";
1984   var len = node.value.length;
1985   node.setSelectionRange (len, len);
1986 }
1987
1988 MIM.debug_print = function (event, ic)
1989 {
1990   if (! MIM.debug)
1991     return;
1992   if (! MIM.debug_nodes)
1993     {
1994       MIM.debug_nodes = new Array ();
1995       MIM.debug_nodes['keydown'] = document.getElementById ('keydown');
1996       MIM.debug_nodes['keypress'] = document.getElementById ('keypress');
1997       MIM.debug_nodes['status0'] = document.getElementById ('status0');
1998       MIM.debug_nodes['status1'] = document.getElementById ('status1');
1999       MIM.debug_nodes['keyseq0'] = document.getElementById ('keyseq0');
2000       MIM.debug_nodes['keyseq1'] = document.getElementById ('keyseq1');
2001       MIM.debug_nodes['preedit0'] = document.getElementById ('preedit0');
2002       MIM.debug_nodes['preedit1'] = document.getElementById ('preedit1');
2003     }
2004   var target = event.target;
2005   var code = event.keyCode;
2006   var ch = event.type == 'keydown' ? 0 : event.charCode;
2007   var key = MIM.decode_key_event (event);
2008   var index;
2009
2010   MIM.debug_nodes[event.type].innerHTML = "" + code + "/" + ch + " : " + key;
2011   index = (event.type == 'keydown' ? '0' : '1');
2012   if (ic)
2013     MIM.debug_nodes['status' + index].innerHTML = ic.im.load_status;
2014   else
2015     MIM.debug_nodes['status' + index].innerHTML = 'no IM';
2016   MIM.debug_nodes['keyseq' + index].innerHTML = ic.keys;
2017   MIM.debug_nodes['preedit' + index].innerHTML = ic.preedit;
2018 };
2019
2020 MIM.get_range = function (target, range)
2021 {
2022   if (target.selectionStart != null) // for Mozilla
2023     {
2024       range[0] = target.selectionStart;
2025       range[1] = target.selectionEnd;
2026     }
2027   else                          // for IE
2028     {
2029       var r = document.selection.createRange ();
2030       var rr = r.duplicate ();
2031
2032       rr.moveToElementText (target);
2033       rr.setEndPoint ('EndToEnd', range);
2034       range[0] = rr.text.length - r.text.length;
2035       range[1] = rr.text.length;
2036     }
2037 }
2038
2039 MIM.set_caret = function (target, ic)
2040 {
2041   if (target.selectionStart != null) // Mozilla
2042     {
2043       target.focus ();
2044       target.setSelectionRange (ic.spot, ic.spot + ic.preedit.length);
2045     }
2046   else                          // IE
2047     {
2048       var range = target.createTextRange ();
2049       range.move ('character', pos);
2050       range.select ();
2051     }
2052 };
2053
2054 (function () {
2055   var range = new Array ();
2056
2057   MIM.check_range = function (target, ic)
2058   {
2059     MIM.get_range (target, range);
2060     if (range[0] != ic.spot || range[1] - range[0] != ic.preedit.length)
2061       {
2062         ic.reset ();
2063         ic.spot = range[0];
2064       }
2065   }
2066 }) ();
2067
2068 MIM.update = function (target, ic, prevlen)
2069 {
2070   var text = target.value;
2071   target.value = (text.substring (0, ic.spot)
2072                   + ic.produced
2073                   + ic.preedit
2074                   + text.substring (ic.spot + prevlen));
2075   ic.spot += ic.produced.length;
2076   MIM.set_caret (target, ic);
2077 };
2078
2079 MIM.reset_ic = function (event)
2080 {
2081   var ic = event.target.mim_ic;
2082   if (ic)
2083     ic.reset ();
2084 };
2085
2086 MIM.keydown = function (event)
2087 {
2088   var target = event.target;
2089   if (! (target.type == "text" || target.type == "textarea"))
2090     return;
2091
2092   var ic = target.mim_ic;
2093   if (! ic || ic.im != MIM.current)
2094     {
2095       ic = new MIM.IC (MIM.current);
2096       target.mim_ic = ic;
2097       MIM.add_event_listener (target, 'blur', MIM.reset_ic);
2098     }
2099   if (ic.im.load_status != MIM.LoadStatus.Loaded)
2100     return;
2101   MIM.check_range (target, ic);
2102   MIM.debug_print (event, ic);
2103   ic.key = MIM.decode_key_event (event);
2104 };
2105
2106 MIM.keypress = function (event)
2107 {
2108   if (! (event.target.type == "text" || event.target.type == "textarea"))
2109     return;
2110
2111   var ic = event.target.mim_ic;
2112   var i;
2113
2114   try {
2115     MIM.log (ic.im.name);
2116     if (ic.im.load_status != MIM.LoadStatus.Loaded)
2117       return;
2118     if (! ic.key)
2119       ic.key = MIM.decode_key_event (event);
2120     if (! ic.key)
2121       {
2122         ic.reset ();
2123         return;
2124       }
2125     
2126     var prevlen = ic.preedit.length;
2127     MIM.log ("filtering " + ic.key);
2128     var result = ic.Filter (ic.key);
2129     MIM.update (target, ic, prevlen);
2130   } finally {
2131     MIM.debug_print (event, ic);
2132   }
2133   return;
2134 };
2135
2136 MIM.select_im = function (event)
2137 {
2138   var target = event.target.parentNode;
2139   while (target.tagName != "SELECT")
2140     target = target.parentNode;
2141   var idx = 0;
2142   var im = false;
2143   for (var lang in MIM.list)
2144     for (var name in MIM.list[lang])
2145       if (idx++ == target.selectedIndex)
2146         {
2147           im = MIM.list[lang][name];
2148           break;
2149         }
2150   document.getElementsByTagName ('body')[0].removeChild (target);
2151   target.target.focus ();
2152   if (im && im != MIM.current)
2153     MIM.current = MIM.load_sync (im);
2154 };
2155
2156 MIM.destroy_menu = function (event)
2157 {
2158   if (event.target.tagName == "SELECT")
2159     document.getElementsByTagName ('body')[0].removeChild (event.target);
2160 };
2161
2162 MIM.select_menu = function (event)
2163 {
2164   var target = event.target;
2165
2166   if (! ((target.type == "text" || target.type == "textarea")
2167          && event.which == 1 && event.ctrlKey))
2168     return;
2169
2170   var sel = document.createElement ('select');
2171   sel.onclick = MIM.select_im;
2172   sel.onmouseout = MIM.destroy_menu;
2173   sel.style.position='absolute';
2174   sel.style.left = (event.clientX - 10) + "px";
2175   sel.style.top = (event.clientY - 10) + "px";
2176   sel.target = target;
2177   var idx = 0;
2178   for (var lang in MIM.list)
2179     for (var name in MIM.list[lang])
2180       {
2181         var option = document.createElement ('option');
2182         var imname = lang + "-" + name;
2183         option.appendChild (document.createTextNode (imname));
2184         option.value = imname;
2185         sel.appendChild (option);
2186         if (MIM.list[lang][name] == MIM.current)
2187           sel.selectedIndex = idx;
2188         idx++;
2189       }
2190   sel.size = idx;
2191   document.getElementsByTagName ('body')[0].appendChild (sel);
2192 };
2193
2194 MIM.test = function ()
2195 {
2196   var im = MIM.im_list['t']['latn-post'];
2197   var ic = new MIM.IC (im);
2198
2199   ic.Filter (new MIM.Key ('a'));
2200   ic.Filter (new MIM.Key ("'"));
2201
2202   if (true)
2203     document.getElementById ('text').value = ic.produced + ic.preedit;
2204   else {
2205     try {
2206       document.getElementById ('text').value
2207         = Xex.ParseTerm (domain, body).Eval (domain).toString ();
2208     } catch (e) {
2209       if (e instanceof Xex.ErrTerm)
2210         alert (e);
2211       throw e;
2212     }
2213   }
2214 }
2215
2216
2217 MIM.init = function ()
2218 {
2219   MIM.add_event_listener (window, 'keydown', MIM.keydown);
2220   MIM.add_event_listener (window, 'keypress', MIM.keypress);
2221   MIM.add_event_listener (window, 'mousedown', MIM.select_menu);
2222   if (window.location == 'http://localhost/mim/index.html')
2223     MIM.server = 'http://localhost/mim';
2224   MIM.current = MIM.im_list['t']['latn-post'];
2225 };
2226
2227 MIM.init_debug = function ()
2228 {
2229   MIM.debug = true;
2230   MIM.init ();
2231 };