*** 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
1583     alert ('handling ' + this.keys.val[this.key_head]);
1584     this.key_head = out.index;
1585     if (sub != this.keymap)
1586       {
1587         restore_state.call (this);
1588         this.keymap = sub;
1589         alert ('submap found, taking map actions:' + sub.actions);
1590         if (this.keymap.actions != null)
1591           {
1592             if (! this.take_actions (this.keymap.actions))
1593               return false;
1594           }
1595         else if (this.keymap.submaps != null)
1596           {
1597             for (var i = this.state_key_head; i < this.key_head; i++)
1598               this.preedit_replace (this.cursor_pos, this.cursor_pos,
1599                                     this.keys.val[i].key, null);
1600           }
1601         if (this.keymap.submaps == null)
1602           {
1603             if (this.keymap.branch_actions != null)
1604               {
1605                 if (! this.take_actions (this.keymap.branch_actions))
1606                   return false;
1607               }
1608             if (this.keymap != this.state.keymap)
1609               this.shift (this.state);
1610           }
1611       }
1612     else
1613       {
1614         var current_state = this.state;
1615
1616         if (this.keymap.branch_actions != null)
1617           {
1618             if (! this.take_actions (this.keymap.branch_actions))
1619               return false;
1620           }
1621         if (this.state == current_state)
1622           {
1623             if (this.state == this.initial_state
1624                 && this.key_head < this.keys.val.length)
1625               return false;
1626             if (this.keymap != this.state.keymap)
1627               this.shift (this.state);
1628             else if (this.keymap.branch_actions == null)
1629               this.shift (this.initial_state);
1630           }
1631       }
1632     return true;
1633   }
1634
1635   proto = {
1636     init: function ()
1637     {
1638       this.produced = null;
1639       this.preedit = '';
1640       this.cursor_pos = 0;
1641       this.marker_positions = {};
1642       this.candidates = null;
1643       this.candidate_show = false;
1644       this.state = null;
1645       this.prev_state = null;
1646       this.initial_state = this.im.initial_state;
1647       this.title = this.initial_state.title;
1648       this.state_preedit = '';
1649       this.state_key_head = 0;
1650       this.state_var_values = {};
1651       this.state_pos = 0;
1652       this.keymap = null;
1653       this.keys = new MIM.KeySeq ();
1654       this.key_head = 0;
1655       this.key_unhandled = false;
1656       this.unhandled_key = null;
1657       this.changed = MIM.ChangedStatus.None;
1658       this.error_message = '';
1659       this.title = this.initial_state.title;
1660       this.produced = null;
1661       this.preedit = '';
1662       this.marker_positions = {};
1663       this.candidate_table = new MIM.CandidateTable ();
1664       this.candidates = null;
1665       this.candidate_show = false;
1666     },
1667
1668     reset: function ()
1669     {
1670       this.init ();
1671       this.state_var_values = {};
1672       this.shift (this.initial_state);
1673     },
1674
1675     catch_args: new Array (Xex.CatchTag._mimtag, null),
1676
1677     take_actions: function (actions)
1678     {
1679       var func_progn = this.domain.GetFunc ('progn');
1680       var func_catch = this.domain.GetFunc ('catch');
1681       this.catch_args[1] = new Xex.Funcall (func_progn, null, actions);
1682       var term = new Xex.Funcall (func_catch, null, this.catch_args);
1683       term = term.Eval (this.domain);
1684       return (! term.IsSymbol || term.val != '@mimtag');
1685     },
1686
1687     GetSurroundingChar: function (pos)
1688     {
1689       if (pos < 0 ? this.caret_pos < - pos : this.target.value.length < pos)
1690         return 0;
1691       return this.target.value.charCodeAt (this.caret_pos + pos);
1692     },
1693     
1694     adjust_markers: function (from, to, inserted)
1695     {
1696       var diff = inserted - (to - from);
1697
1698       for (var m in this.marker_positions)
1699         if (this.marker_positions[m] > from)
1700           this.marker_positions[m] = (this.marker_positions[m] >= to
1701                                       ? pos + diff : from);
1702       if (this.cursor_pos >= to)
1703         set_cursor.call (this, 'adjust', this.cursor_pos + diff);
1704       else if (this.cursor_pos > from)
1705         set_cursor.call (this, 'adjust', from)
1706     },
1707
1708     preedit_replace: function (from, to, text, candidates)
1709     {
1710       this.preedit = (this.preedit.substring (0, from)
1711                       + text + this.preedit.substring (to));
1712       this.adjust_markers (from, to, text.length);
1713       this.candidate_table.adjust (from, to, text.length);
1714       if (candidates)
1715         this.candidate_table.put (from, from + text.length, candidates)
1716     },
1717
1718     insert: function (text, candidates)
1719     {
1720       this.preedit_replace (this.cursor_pos, this.cursor_pos, text, candidates);
1721       this.changed = MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos;
1722     },
1723
1724     del: function (pos)
1725     {
1726       if (pos < 0)
1727         {
1728           this.DelSurroundText (pos);
1729           pos = 0;
1730         }
1731       else if (pos > this.preedit.length)
1732         {
1733           this.DelSurroundText (pos - this.preedit.length);
1734           pos = this.preedit.length;
1735         }
1736       if  (pos < this.cursor_pos)
1737         this.preedit = (this.predit.substring (0, pos)
1738                         + this.preedit.substring (this.cursor_pos));
1739       else
1740         this.preedit = (this.preedit.substring (0, this.cursor_pos)
1741                         + this.predit.substring (pos));
1742     },
1743
1744     show: function ()
1745     {
1746       this.candidate_show = true;
1747       this.changed |= MIM.ChangedStatus.CandidateShow;
1748     },
1749
1750     hide: function ()
1751     {
1752       this.candidate_show = false;
1753       this.changed |= MIM.ChangedStatus.CandidateShow;
1754     },
1755
1756     move: function (pos)
1757     {
1758       if (pos < 0)
1759         pos = 0;
1760       else if (pos > this.preedit.length)
1761         pos = this.preedit.length;
1762       if (pos != this.cursor_pos)
1763         {
1764           set_cursor.call (this, 'move', pos);
1765           this.changed |= MIM.ChangedStatus.Preedit;
1766         }
1767     },
1768
1769     pushback: function (n)
1770     {
1771       if (n instanceof MIM.KeySeq)
1772         {
1773           if (this.key_head > 0)
1774             this.key_head--;
1775           if (this.key_head < this.keys.val.length)
1776             this.keys.val.splice (this.key_head,
1777                                   this.keys.val.length - this.key_head);
1778           for (var i = 0; i < n.val.length; i++)
1779             this.keys.val.push (n.val[i]);
1780           return;
1781         }
1782       if (n > 0)
1783         {
1784           this.key_head -= n;
1785           if (this.key_head < 0)
1786             this.key_head = 0;
1787         }
1788       else if (n == 0)
1789         this.key_head = 0;
1790       else
1791       {
1792         this.key_head = - n;
1793         if (this.key_head > this.keys.val.length)
1794           this.key_head = this.keys.val.length;
1795       }
1796     },
1797
1798     pop: function ()
1799     {
1800       if (this.key_head < this.keys.val.length)
1801         this.keys.val.splice (this.key_head, 1);
1802     },
1803
1804     commit: function ()
1805     {
1806       if (this.preedit.length > 0)
1807       {
1808         this.candidate_table.clear ();
1809         this.produced += this.preedit;
1810         this.preedit_replace.call (this, 0, this.preedit.Length, '', null);
1811       }
1812     },
1813
1814     shift: function (state)
1815     {
1816       if (state == null)
1817       {
1818         if (this.prev_state == null)
1819           return;
1820         state = this.prev_state;
1821       }
1822
1823       if (state == this.initial_state)
1824         {
1825           this.commit ();
1826           this.keys.val.splice (0, this.key_head);
1827           this.key_head = 0;
1828           if (state != this.state)
1829             {
1830               this.domain.RestoreValues (this.state_initial_var_values);
1831               if (state.enter_actions != null)
1832                 take_actions.call (state.enter_actions);
1833             }
1834           this.prev_state = null;
1835         }
1836       else
1837         {
1838           if (state != this.state && state.enter_actions != null)
1839             take_actions.call (state.enter_actions);
1840           this.prev_state = this.state;
1841         }
1842       save_state.call (this);
1843       if (! this.state || this.state.title != state.title)
1844         this.changed |= MIM.ChangedStatus.StateTitle;
1845       this.state = state;
1846       this.keymap = state.keymap;
1847     },
1848
1849     Filter: function (key)
1850     {
1851       if (! this.active)
1852         {
1853           this.key_unhandled = true;
1854           this.unhandled_key = key;
1855           return false;
1856         }
1857       if (key.key == '_reload')
1858         return true;
1859       this.changed = MIM.ChangedStatus.None;
1860       this.produced = '';
1861       this.key_unhandled = false;
1862       this.keys.val.push (key);
1863       var count = 0;
1864       while (this.key_head < this.keys.val.length)
1865         {
1866           if (! handle_key.call (this))
1867             {
1868               this.unhandled_key = this.keys.val[this.key_head++];
1869               this.key_unhandled = true;
1870               break;
1871             }
1872           if (++count == 10)
1873             break;
1874         }
1875       this.keys.val.splice (0, this.key_head);
1876       this.key_head = 0;
1877       return (! this.key_unhandled && this.produced.length == 0);
1878     }
1879   }
1880
1881   MIM.IC.prototype = proto;
1882
1883   var node = Xex.Load (null, "imlist.xml");
1884   for (node = node.firstChild; node; node = node.nextSibling)
1885     if (node.nodeName == 'input-method')
1886       {
1887         var lang, name, extra_id, file;
1888
1889         for (var n = node.firstChild; n; n = n.nextSibling)
1890           {
1891             if (n.nodeName == 'language')
1892               lang = n.firstChild.nodeValue;
1893             else if (n.nodeName == 'name')
1894               name = n.firstChild.nodeValue;
1895             else if (n.nodeName == 'extra-id')
1896               extra_id = n.firstChild.nodeValue;
1897             else if (n.nodeName == 'filename')
1898               file = n.firstChild.nodeValue;
1899           }
1900         if (! MIM.im_list[lang])
1901           MIM.im_list[lang] = {};
1902         MIM.im_list[lang][name] = new MIM.IM (lang, name, extra_id, file);
1903       }
1904   node = undefined;
1905 }) ();
1906
1907 (function () {
1908   var keys = new Array ();
1909   keys[0x09] = 'tab';
1910   keys[0x08] = 'backspace';
1911   keys[0x0D] = 'return';
1912   keys[0x1B] = 'escape';
1913   keys[0x20] = 'space';
1914   keys[0x21] = 'pageup';
1915   keys[0x22] = 'pagedown';
1916   keys[0x23] = 'end';
1917   keys[0x24] = 'home';
1918   keys[0x25] = 'left';
1919   keys[0x26] = 'up';
1920   keys[0x27] = 'right';
1921   keys[0x28] = 'down';
1922   keys[0x2D] = 'insert';
1923   keys[0x2E] = 'delete';
1924   for (var i = 1; i <= 12; i++)
1925     keys[111 + i] = "f" + i;
1926   keys[0x90] = "numlock";
1927   keys[0xF0] = "capslock";
1928
1929   MIM.decode_key_event = function (event)
1930   {
1931     var key = ((event.type == 'keydown' || event.keyCode) ? event.keyCode
1932                : event.charCode ? event.charCode
1933                : false);
1934     if (! key)
1935       return false;
1936     if (event.type == 'keydown')
1937       {
1938         key = keys[key];
1939         if (! key)
1940           return false;
1941         if (event.shiftKey) key = "S-" + key ;
1942       }
1943     else
1944       key = String.fromCharCode (key);
1945     if (event.altKey) key = "A-" + key ;
1946     if (event.ctrlKey) key = "C-" + key ;
1947     return new MIM.Key (key);
1948   }
1949 }) ();
1950
1951 MIM.add_event_listener
1952   = (window.addEventListener
1953      ? function (target, type, listener) {
1954        target.addEventListener (type, listener, false);
1955      }
1956      : window.attachEvent
1957      ? function (target, type, listener) {
1958        target.attachEvent ('on' + type,
1959                            function() {
1960                              listener.call (target, window.event);
1961                            });
1962      }
1963      : function (target, type, listener) {
1964        target['on' + type]
1965          = function (e) { listener.call (target, e || window.event); };
1966      });
1967
1968 MIM.debug_print = function (event, ic)
1969 {
1970   if (! MIM.debug)
1971     return;
1972   if (! MIM.debug_nodes)
1973     {
1974       MIM.debug_nodes = new Array ();
1975       MIM.debug_nodes['keydown'] = document.getElementById ('keydown');
1976       MIM.debug_nodes['keypress'] = document.getElementById ('keypress');
1977       MIM.debug_nodes['status0'] = document.getElementById ('status0');
1978       MIM.debug_nodes['status1'] = document.getElementById ('status1');
1979       MIM.debug_nodes['keyseq0'] = document.getElementById ('keyseq0');
1980       MIM.debug_nodes['keyseq1'] = document.getElementById ('keyseq1');
1981       MIM.debug_nodes['range0'] = document.getElementById ('range0');
1982       MIM.debug_nodes['range1'] = document.getElementById ('range1');
1983     }
1984   var target = event.target;
1985   var code = event.keyCode;
1986   var ch = event.type == 'keydown' ? 0 : event.charCode;
1987   var key = MIM.decode_key_event (event);
1988   var index;
1989
1990   MIM.debug_nodes[event.type].innerHTML = "" + code + "/" + ch + " : " + key;
1991   index = (event.type == 'keydown' ? '0' : '1');
1992   MIM.debug_nodes['status' + index].innerHTML = ic.im.load_status;
1993   MIM.debug_nodes['keyseq' + index].innerHTML = ic.keys;
1994   MIM.debug_nodes['preedit' + index].innerHTML = ic.preedit;
1995 };
1996
1997 MIM.get_range = function (target, range)
1998 {
1999   if (target.selectionStart != null) // for Mozilla
2000     {
2001       range[0] = target.selectionStart;
2002       range[1] = target.selectionEnd;
2003     }
2004   else                          // for IE
2005     {
2006       var r = document.selection.createRange ();
2007       var rr = r.duplicate ();
2008
2009       rr.moveToElementText (target);
2010       rr.setEndPoint ('EndToEnd', range);
2011       range[0] = rr.text.length - r.text.length;
2012       range[1] = rr.text.length;
2013     }
2014 }
2015
2016 MIM.set_caret = function (target, ic)
2017 {
2018   if (target.selectionStart != null) // Mozilla
2019     {
2020       target.focus ();
2021       target.setSelectionRange (ic.spot, ic.spot + ic.preedit.length);
2022     }
2023   else                          // IE
2024     {
2025       var range = target.createTextRange ();
2026       range.move ('character', pos);
2027       range.select ();
2028     }
2029 };
2030
2031 (function () {
2032   var range = new Array ();
2033
2034   MIM.check_range = function (target, ic)
2035   {
2036     MIM.get_range (target, range);
2037     if (range[0] != ic.spot || range[1] - range[0] != ic.preedit.length)
2038       {
2039         ic.reset ();
2040         ic.spot = range[0];
2041       }
2042   }
2043 };
2044
2045 MIM.produce = function (target, ic)
2046 {
2047   target.value = (text.substring (0, ic.spot)
2048                   + ic.produced
2049                   + text.substring (ic.));
2050   ic.range[1] = ic.range[0] + insert.length;
2051   MIM.set_caret (ic.target, ic.range[1]);
2052 };
2053
2054 MIM.reset_ic = function (event)
2055 {
2056   var ic = event.target.mim_ic;
2057   if (ic)
2058     ic.reset ();
2059 };
2060
2061 MIM.keydown = function (event)
2062 {
2063   var target = event.target;
2064   if (! (target.type == "text" || target.type == "textarea"))
2065     return;
2066
2067   var ic = target.mim_ic;
2068   if (! ic || ic.im != MIM.current)
2069     {
2070       ic = new MIM.IC (MIM.current_im);
2071       target.mim_ic = ic;
2072       MIM.add_event_listener (target, 'blur', MIM.reset_ic);
2073     }
2074   if (ic.im.load_status != MIM.LoadStatus.Loaded)
2075     return;
2076   MIM.check_range (target, ic);
2077   MIM.debug_print (event, ic);
2078   ic.key = MIM.decode_key_event (event);
2079 };
2080
2081 MIM.keypress = function (event)
2082 {
2083   if (! (event.target.type == "text" || event.target.type == "textarea"))
2084     return;
2085
2086   var ic = event.target.mim_ic;
2087   var i;
2088
2089   try {
2090     if (ic.im.loaded != MIM.LoadStatus.Loaded)
2091       return;
2092     if (! ic.key)
2093       ic.key = MIM.decode_key_event (event);
2094     if (! ic.key)
2095       {
2096         ic.reset ();
2097         return;
2098       }
2099     if (ic.Filter (ic.key))
2100       MIM.set_caret (target, ic);
2101     else
2102       {
2103         if (ic.preedit.length > 0)
2104           {
2105             MIM.insert
2106
2107           }
2108       }
2109
2110
2111     if (ic.im.status == 1) // Still loading.
2112       return;
2113     MIM.handle_keyseq (event, ic);
2114   } finally {
2115     MIM.debug_print (event, ic);
2116   }
2117   return;
2118 };
2119
2120 MIM.select_im = function (event)
2121 {
2122   var target = event.target.parentNode;
2123   while (target.tagName != "SELECT")
2124     target = target.parentNode;
2125   var idx = 0;
2126   var im = false;
2127   for (var lang in MIM.list)
2128     for (var name in MIM.list[lang])
2129       if (idx++ == target.selectedIndex)
2130         {
2131           im = MIM.list[lang][name];
2132           break;
2133         }
2134   document.getElementsByTagName ('body')[0].removeChild (target);
2135   target.target.focus ();
2136   if (im && im != MIM.current_im)
2137     MIM.current_im = MIM.load_sync (im);
2138 };
2139
2140 MIM.destroy_menu = function (event)
2141 {
2142   if (event.target.tagName == "SELECT")
2143     document.getElementsByTagName ('body')[0].removeChild (event.target);
2144 };
2145
2146 MIM.select_menu = function (event)
2147 {
2148   var target = event.target;
2149
2150   if (! ((target.type == "text" || target.type == "textarea")
2151          && event.which == 1 && event.ctrlKey))
2152     return;
2153
2154   var sel = document.createElement ('select');
2155   sel.onclick = MIM.select_im;
2156   sel.onmouseout = MIM.destroy_menu;
2157   sel.style.position='absolute';
2158   sel.style.left = (event.clientX - 10) + "px";
2159   sel.style.top = (event.clientY - 10) + "px";
2160   sel.target = target;
2161   var idx = 0;
2162   for (var lang in MIM.list)
2163     for (var name in MIM.list[lang])
2164       {
2165         var option = document.createElement ('option');
2166         var imname = lang + "-" + name;
2167         option.appendChild (document.createTextNode (imname));
2168         option.value = imname;
2169         sel.appendChild (option);
2170         if (MIM.list[lang][name] == MIM.current_im)
2171           sel.selectedIndex = idx;
2172         idx++;
2173       }
2174   sel.size = idx;
2175   document.getElementsByTagName ('body')[0].appendChild (sel);
2176 };
2177
2178
2179
2180 MIM.test = function ()
2181 {
2182   var im = MIM.im_list['t']['latn-post'];
2183   var ic = new MIM.IC (im);
2184
2185   ic.Filter (new MIM.Key ('a'));
2186   ic.Filter (new MIM.Key ("'"));
2187
2188   if (true)
2189     document.getElementById ('text').value = ic.produced + ic.preedit;
2190   else {
2191     try {
2192       document.getElementById ('text').value
2193         = Xex.ParseTerm (domain, body).Eval (domain).toString ();
2194     } catch (e) {
2195       if (e instanceof Xex.ErrTerm)
2196         alert (e);
2197       throw e;
2198     }
2199   }
2200 }