*** 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;
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   }
1501
1502   MIM.CandidateTable = function ()
1503   {
1504     this.table = new Array ();
1505   }
1506
1507   MIM.CandidateTable.prototype.get = function (from)
1508   {
1509     for (var i = 0; i < this.table.length; i++)
1510       {
1511         var elt = this.table[i];
1512         if (elt.from <= from && elt.to > from)
1513           return elt.val;
1514       }
1515   }
1516
1517   MIM.CandidateTable.prototype.put = function (from, to, candidates)
1518   {
1519     for (var i = 0; i < this.table.length; i++)
1520       {
1521         var elt = this.table[i];
1522         if (elt.from >= from && elt.from < to
1523             || elt.to >= from && elt.to < to)
1524           {
1525             elt.from = from;
1526             elt.to = to;
1527             elt.val = candidates;
1528             return;
1529           }
1530       }
1531     this.table.push ({ from: from, to: to, val: candidates });
1532   }
1533
1534   MIM.CandidateTable.prototype.adjust = function (from, to, inserted)
1535   {
1536     var diff = inserted - (to - from);
1537     for (var i = 0; i < this.table.length; i++)
1538       {
1539         var elt = this.table[i];
1540         if (elt.from >= to)
1541           {
1542             elt.from += diff;
1543             elt.to += diff;
1544           }
1545       }
1546   }
1547
1548   MIM.CandidateTable.prototype.clear = function ()
1549   {
1550     this.table.length = 0;
1551   }
1552
1553   function set_cursor (prefix, pos)
1554   {
1555     this.cursor_pos = pos;
1556     if (pos > 0)
1557       this.candidates = this.candidate_table.get (pos - 1);
1558     else
1559       this.candidates = null;
1560   }
1561
1562   function save_state ()
1563   {
1564     this.state_var_values = this.domain.SaveValues ();
1565     this.state_preedit = this.preedit;
1566     this.state_key_head = this.key_head;
1567     this.state_pos = this.cursor_pos;
1568   }
1569
1570   function restore_state ()
1571   {
1572     this.domain.RestoreValues (this.state_var_values);
1573     this.preedit = this.state_preedit;
1574     set_cursor.call (this, "restore", this.state_pos);
1575   }
1576
1577   function handle_key ()
1578   {
1579     var out = this.keymap.Lookup (this.keys, this.key_head);
1580     var sub = out.map;
1581
1582     alert ('handling ' + this.keys.val[this.key_head]);
1583     this.key_head = out.index;
1584     if (sub != this.keymap)
1585       {
1586         restore_state.call (this);
1587         this.keymap = sub;
1588         alert ('submap found, taking map actions:' + sub.actions);
1589         if (this.keymap.actions != null)
1590           {
1591             if (! this.take_actions (this.keymap.actions))
1592               return false;
1593           }
1594         else if (this.keymap.submaps != null)
1595           {
1596             for (var i = this.state_key_head; i < this.key_head; i++)
1597               this.preedit_replace (this.cursor_pos, this.cursor_pos,
1598                                     this.keys.val[i].key, null);
1599           }
1600         if (this.keymap.submaps == null)
1601           {
1602             if (this.keymap.branch_actions != null)
1603               {
1604                 if (! this.take_actions (this.keymap.branch_actions))
1605                   return false;
1606               }
1607             if (this.keymap != this.state.keymap)
1608               this.shift (this.state);
1609           }
1610       }
1611     else
1612       {
1613         var current_state = this.state;
1614
1615         if (this.keymap.branch_actions != null)
1616           {
1617             if (! this.take_actions (this.keymap.branch_actions))
1618               return false;
1619           }
1620         if (this.state == current_state)
1621           {
1622             if (this.state == this.initial_state
1623                 && this.key_head < this.keys.val.length)
1624               return false;
1625             if (this.keymap != this.state.keymap)
1626               this.shift (this.state);
1627             else if (this.keymap.branch_actions == null)
1628               this.shift (this.initial_state);
1629           }
1630       }
1631     return true;
1632   }
1633
1634   proto = {
1635     init: function ()
1636     {
1637       this.produced = null;
1638       this.preedit = '';
1639       this.cursor_pos = 0;
1640       this.marker_positions = {};
1641       this.candidates = null;
1642       this.candidate_show = false;
1643       this.state = null;
1644       this.prev_state = null;
1645       this.initial_state = this.im.initial_state;
1646       this.title = this.initial_state.title;
1647       this.state_preedit = '';
1648       this.state_key_head = 0;
1649       this.state_var_values = {};
1650       this.state_pos = 0;
1651       this.keymap = null;
1652       this.keys = new MIM.KeySeq ();
1653       this.key_head = 0;
1654       this.key_unhandled = false;
1655       this.unhandled_key = null;
1656       this.changed = MIM.ChangedStatus.None;
1657       this.error_message = '';
1658       this.title = this.initial_state.title;
1659       this.produced = null;
1660       this.preedit = '';
1661       this.marker_positions = {};
1662       this.candidate_table = new MIM.CandidateTable ();
1663       this.candidates = null;
1664       this.candidate_show = false;
1665     },
1666
1667     reset: function ()
1668     {
1669       this.init ();
1670       this.state_var_values = {};
1671       this.shift (this.initial_state);
1672     },
1673
1674     catch_args: new Array (Xex.CatchTag._mimtag, null),
1675
1676     take_actions: function (actions)
1677     {
1678       var func_progn = this.domain.GetFunc ('progn');
1679       var func_catch = this.domain.GetFunc ('catch');
1680       this.catch_args[1] = new Xex.Funcall (func_progn, null, actions);
1681       var term = new Xex.Funcall (func_catch, null, this.catch_args);
1682       term = term.Eval (this.domain);
1683       return (! term.IsSymbol || term.val != '@mimtag');
1684     },
1685
1686     GetSurroundingChar: function (pos)
1687     {
1688       if (pos < 0 ? this.caret_pos < - pos : this.target.value.length < pos)
1689         return 0;
1690       return this.target.value.charCodeAt (this.caret_pos + pos);
1691     },
1692     
1693     adjust_markers: function (from, to, inserted)
1694     {
1695       var diff = inserted - (to - from);
1696
1697       for (var m in this.marker_positions)
1698         if (this.marker_positions[m] > from)
1699           this.marker_positions[m] = (this.marker_positions[m] >= to
1700                                       ? pos + diff : from);
1701       if (this.cursor_pos >= to)
1702         set_cursor.call (this, 'adjust', this.cursor_pos + diff);
1703       else if (this.cursor_pos > from)
1704         set_cursor.call (this, 'adjust', from)
1705     },
1706
1707     preedit_replace: function (from, to, text, candidates)
1708     {
1709       this.preedit = (this.preedit.substring (0, from)
1710                       + text + this.preedit.substring (to));
1711       this.adjust_markers (from, to, text.length);
1712       this.candidate_table.adjust (from, to, text.length);
1713       if (candidates)
1714         this.candidate_table.put (from, from + text.length, candidates)
1715     },
1716
1717     insert: function (text, candidates)
1718     {
1719       this.preedit_replace (this.cursor_pos, this.cursor_pos, text, candidates);
1720       this.changed = MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos;
1721     },
1722
1723     del: function (pos)
1724     {
1725       if (pos < 0)
1726         {
1727           this.DelSurroundText (pos);
1728           pos = 0;
1729         }
1730       else if (pos > this.preedit.length)
1731         {
1732           this.DelSurroundText (pos - this.preedit.length);
1733           pos = this.preedit.length;
1734         }
1735       if  (pos < this.cursor_pos)
1736         this.preedit = (this.predit.substring (0, pos)
1737                         + this.preedit.substring (this.cursor_pos));
1738       else
1739         this.preedit = (this.preedit.substring (0, this.cursor_pos)
1740                         + this.predit.substring (pos));
1741     },
1742
1743     show: function ()
1744     {
1745       this.candidate_show = true;
1746       this.changed |= MIM.ChangedStatus.CandidateShow;
1747     },
1748
1749     hide: function ()
1750     {
1751       this.candidate_show = false;
1752       this.changed |= MIM.ChangedStatus.CandidateShow;
1753     },
1754
1755     move: function (pos)
1756     {
1757       if (pos < 0)
1758         pos = 0;
1759       else if (pos > this.preedit.length)
1760         pos = this.preedit.length;
1761       if (pos != this.cursor_pos)
1762         {
1763           set_cursor.call (this, 'move', pos);
1764           this.changed |= MIM.ChangedStatus.Preedit;
1765         }
1766     },
1767
1768     pushback: function (n)
1769     {
1770       if (n instanceof MIM.KeySeq)
1771         {
1772           if (this.key_head > 0)
1773             this.key_head--;
1774           if (this.key_head < this.keys.val.length)
1775             this.keys.val.splice (this.key_head,
1776                                   this.keys.val.length - this.key_head);
1777           for (var i = 0; i < n.val.length; i++)
1778             this.keys.val.push (n.val[i]);
1779           return;
1780         }
1781       if (n > 0)
1782         {
1783           this.key_head -= n;
1784           if (this.key_head < 0)
1785             this.key_head = 0;
1786         }
1787       else if (n == 0)
1788         this.key_head = 0;
1789       else
1790       {
1791         this.key_head = - n;
1792         if (this.key_head > this.keys.val.length)
1793           this.key_head = this.keys.val.length;
1794       }
1795     },
1796
1797     pop: function ()
1798     {
1799       if (this.key_head < this.keys.val.length)
1800         this.keys.val.splice (this.key_head, 1);
1801     },
1802
1803     commit: function ()
1804     {
1805       if (this.preedit.length > 0)
1806       {
1807         this.candidate_table.clear ();
1808         this.produced += this.preedit;
1809         this.preedit_replace.call (this, 0, this.preedit.Length, '', null);
1810       }
1811     },
1812
1813     shift: function (state)
1814     {
1815       if (state == null)
1816       {
1817         if (this.prev_state == null)
1818           return;
1819         state = this.prev_state;
1820       }
1821
1822       if (state == this.initial_state)
1823         {
1824           this.commit ();
1825           this.keys.val.splice (0, this.key_head);
1826           this.key_head = 0;
1827           if (state != this.state)
1828             {
1829               this.domain.RestoreValues (this.state_initial_var_values);
1830               if (state.enter_actions != null)
1831                 take_actions.call (state.enter_actions);
1832             }
1833           this.prev_state = null;
1834         }
1835       else
1836         {
1837           if (state != this.state && state.enter_actions != null)
1838             take_actions.call (state.enter_actions);
1839           this.prev_state = this.state;
1840         }
1841       save_state.call (this);
1842       if (! this.state || this.state.title != state.title)
1843         this.changed |= MIM.ChangedStatus.StateTitle;
1844       this.state = state;
1845       this.keymap = state.keymap;
1846     },
1847
1848     Filter: function (key)
1849     {
1850       if (! this.active)
1851         {
1852           this.key_unhandled = true;
1853           this.unhandled_key = key;
1854           return false;
1855         }
1856       if (key.key == '_reload')
1857         return true;
1858       this.changed = MIM.ChangedStatus.None;
1859       this.produced = '';
1860       this.key_unhandled = false;
1861       this.keys.val.push (key);
1862       var count = 0;
1863       while (this.key_head < this.keys.val.length)
1864         {
1865           if (! handle_key.call (this))
1866             {
1867               this.unhandled_key = this.keys.val[this.key_head++];
1868               this.key_unhandled = true;
1869               break;
1870             }
1871           if (++count == 10)
1872             break;
1873         }
1874       this.keys.val.splice (0, this.key_head);
1875       this.key_head = 0;
1876       return (! this.key_unhandled && this.produced.length == 0);
1877     }
1878   }
1879
1880   MIM.IC.prototype = proto;
1881
1882   var node = Xex.Load (null, "imlist.xml");
1883   for (node = node.firstChild; node; node = node.nextSibling)
1884     if (node.nodeName == 'input-method')
1885       {
1886         var lang, name, extra_id, file;
1887
1888         for (var n = node.firstChild; n; n = n.nextSibling)
1889           {
1890             if (n.nodeName == 'language')
1891               lang = n.firstChild.nodeValue;
1892             else if (n.nodeName == 'name')
1893               name = n.firstChild.nodeValue;
1894             else if (n.nodeName == 'extra-id')
1895               extra_id = n.firstChild.nodeValue;
1896             else if (n.nodeName == 'filename')
1897               file = n.firstChild.nodeValue;
1898           }
1899         if (! MIM.im_list[lang])
1900           MIM.im_list[lang] = {};
1901         MIM.im_list[lang][name] = new MIM.IM (lang, name, extra_id, file);
1902       }
1903   node = undefined;
1904 }) ();
1905
1906 (function () {
1907
1908   MIMTEST = function (name) { this.nam = name; };
1909
1910   function testshow () { alert (this.nam); };
1911
1912   MIMTEST.prototype.show = function () { testshow.call (this); }
1913   MIMTEST.prototype.cut = function (from, to) {
1914     this.val.splice (from, to -from); return this.val; }
1915
1916   MIMTEST2 = function (name) { this.nam = name;
1917                                this.val = new Array (1,2,3,4,5);}
1918
1919   MIMTEST2.prototype = MIMTEST.prototype;
1920
1921   var x = new MIMTEST2 ('test2');
1922 }) ();
1923
1924 MIM.test = function ()
1925 {
1926   var im = MIM.im_list['t']['latn-post'];
1927   var ic = new MIM.IC (im);
1928
1929   document.AIM = im;
1930
1931   ic.Filter (new MIM.Key ('a'));
1932   ic.Filter (new MIM.Key ("'"));
1933
1934   if (true)
1935     document.getElementById ('text').value = ic.preedit;
1936   else {
1937     try {
1938       document.getElementById ('text').value
1939         = Xex.ParseTerm (domain, body).Eval (domain).toString ();
1940     } catch (e) {
1941       if (e instanceof Xex.ErrTerm)
1942         alert (e);
1943       throw e;
1944     }
1945   }
1946 }