ccd6e653252ab38d71df151959bcd6e3266011b7
[m17n/m17n-lib-js.git] / xex.js
1 // xex.js -- Xex (XML Expression) interpreter
2 // Copyright (C) 2010
3 //   National Institute of Advanced Industrial Science and Technology (AIST)
4 //   Registration Number H15PRO112
5
6 // This file is part of the m17n database; a sub-part of the m17n
7 // library.
8
9 // The m17n library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public License
11 // as published by the Free Software Foundation; either version 2.1 of
12 // the License, or (at your option) any later version.
13
14 // The m17n library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 // Lesser General Public License for more details.
18
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with the m17n library; if not, write to the Free
21 // Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 // Boston, MA 02110-1301, USA.
23
24 // Please note that the code is not yet matured.
25
26 var Xex = {};
27
28 (function () {                  // Logging
29   // The logging node containing tracing information.
30   var log = null;
31   // Number of lines.
32   var lines;
33   // Style properties of the logging node.
34   var styles = { border: '1px solid black',
35                  font: 'normal normal normal small monospace',
36                  width: '100%',
37                  minHeight: '300px',
38                  maxHeight: '300px',
39                  overflow: 'auto' };
40   
41   // Toggle logging on and off.  PARENT if non-null specifies the
42   // parent of the log node.  The log node is appended to PARENT.
43   // If PARENT is null, 'body' node is assumed.
44   Xex.LogToggle = function (parent)
45   {
46     if (log)
47       {
48         log.parentNode.removeChild (log);
49         log = null;
50         return;
51       }
52     if (! parent)
53       parent = document.getElementsByTagName ('body')[0];
54     log = document.createElement ('ol');
55     for (var prop in styles)
56       log.style[prop] = styles[prop];
57     parent.appendChild (log);
58     lines = 0;
59     return log;
60   }
61
62   // Log ARG (string).  INDENT if specified is a number of columns to
63   // indent.  If INDENT is -1, ARG is appended to the last log.
64   Xex.Log = function (arg, indent)
65   {
66     if (! log)
67       return;
68     if (! arg)
69       {
70         while (log.childNodes.length > 0)
71           log.removeChild (log.firstChild);
72         lines = 0;
73       }
74     else
75       {
76         var node;
77         if (indent == -1)
78           log.lastChild.innerText += arg;
79         else
80           {
81             lines++;
82             if (lines >= 256)
83               {
84                 node = log.firstElement ();
85                 log.start = lines - 254;
86               }
87             else
88               node = document.createElement ('li');
89             if (indent != undefined)
90               node.style.textIndent = (indent + 1) + 'em';
91             else
92               node.style.textIndent = '0px';
93             node.innerText = arg;
94             log.appendChild (node);
95             log.scrollTop = log.scrollHeight;
96           }
97       }
98   }
99 }) ();
100
101 Xex.Error = {
102   UnknownError: "unknown-error",
103   WrongArgument: "wrong-argument",
104   // Load time errors.
105   InvalidInteger: "invalid-integer",
106   TermTypeInvalid: "term-type-invalid",
107   FunctionConflict: "function-conflict",
108   VariableTypeConflict: "variable-type-conflict",
109   VariableRangeConflict: "variable-range-conflict",
110   VariableWrongRange: "variable-wrong-range",
111   VariableWrongValue: "variable-wrong-value",
112
113   UnknownFunction: "unknown-function",
114   MacroExpansionError: "macro-expansion-error",
115   NoVariableName: "no-variable-name",
116   NoFunctionName: "no-funcion-name",
117
118   // Run time errors.
119   ArithmeticError: "arithmetic-error",
120   WrongType: "wrong-type",
121   IndexOutOfRange: "index-out-of-range",
122   ValueOutOfRange: "value-out-of-range",
123   NoLoopToBreak: "no-loop-to-break",
124   UncaughtThrow: "uncaught-throw"
125 };
126
127 Xex.Variable = function (name, desc, val, range)
128 {
129   if (name)
130     this.name = name;
131   if (desc)
132     this.desc = desc;
133   this.val = val;
134   if (range)
135     this.range = range;
136 }
137
138 Xex.Variable.prototype = {
139   clone: function ()
140   {
141     return new Xex.Variable (this.name, this.desc, this.val, this.range);
142   },
143   equals: function (obj)
144   {
145     return ((obj instanceof Xex.Variable)
146             && obj.name == this.name);
147   },
148   SetValue: function (term)
149   {
150     this.val = term;
151     return term;
152   }
153 }  
154
155
156 Xex.Function = function (name, with_var, min_args, max_args)
157 {
158   this.name = name;
159   this.with_var = with_var;
160   this.min_args = min_args;
161   this.max_args = max_args;
162 };  
163
164 Xex.Subrountine = function (builtin, name, with_var, min_args, max_args)
165 {
166   Xex.Function.apply (this, [name, with_var, min_args, max_args]);
167   this.builtin = builtin;
168 }
169
170 Xex.Subrountine.prototype.Call = function (domain, vari, args)
171 {
172   var newargs = new Array ();
173   for (var i = 0; i < args.length; i++)
174     {
175       newargs[i] = args[i].Eval (domain);
176       if (domain.Thrown ())
177         return newargs[i];
178     }
179   return this.builtin (domain, vari, newargs)
180 }
181
182 Xex.SpecialForm = function (builtin, name, with_var, min_args, max_args)
183 {
184   Xex.Function.apply (this, [name, with_var, min_args, max_args]);
185   this.builtin = builtin;
186 }
187
188 Xex.SpecialForm.prototype.Call = function (domain, vari, args)
189 {
190   return this.builtin (domain, vari, args)
191 }
192
193 Xex.Lambda = function (name, min_args, max_args, args, body)
194 {
195   Xex.Function.apply (this, [name, false, min_args, max_args]);
196   this.args = args;
197   this.body = body;
198 }
199
200 Xex.Lambda.prototype.Call = function (domain, vari, args)
201 {
202   var current = domain.bindings;
203   var result = Xex.Zero;
204   var limit = max_args >= 0 ? args.length : args.length - 1;
205   var i;
206   
207   try {
208     for (i = 0; i < limit; i++)
209       {
210         result = args[i].Eval (domain);
211         if (domain.Thrown ())
212           return result;
213         domain.Bind (this.args[i], result);
214       }
215     if (max_args < 0)
216       {
217         var list = new Array ();
218         for (i = 0; i < args[limit].length; i++)
219           {
220             result = args[limit].Eval (domain);
221             if (domain.Thrown ())
222               return result;
223             list[i] = result;
224           }
225         domain.Bind (this.args[limit], list);
226       }
227     try {
228       domain.Catch (Xex.CatchTag.Return);
229       for (var term in this.body)
230         {
231           result = term.Eval (domain);
232           if (domain.Thrown ())
233             return result;
234         }
235     } finally {
236       domain.Uncatch ();
237     }
238   } finally {
239     domain.UnboundTo (current);
240   }
241   return result;
242 }
243
244 Xex.Macro = function (name, min_args, max_args, args, body)
245 {
246   Xex.Function.apply (this, [name, false, min_args, max_args]);
247   this.args = args;
248   this.body = body;
249 }
250
251 Xex.Macro.prototype.Call = function (domain, vari, args)
252 {
253   var current = domain.bindings;
254   var result = Xex.Zero;
255   var i;
256
257   try {
258     for (i = 0; i < args.length; i++)
259       domain.Bind (this.args[i], args[i]);
260     try {
261       domain.Catch (Xex.CatchTag.Return);
262       for (var i in this.body)
263         {
264           result = this.body[i].Eval (domain);
265           if (domain.Thrown ())
266             break;
267         }
268     } finally {
269       domain.Uncatch ();
270     }
271   } finally {
272     domain.UnboundTo (current);
273   }
274   return result;
275 }
276
277 Xex.Bindings = function (vari)
278 {
279   this.vari = vari;
280   this.old_value = vari.val;
281 }
282
283 Xex.Bindings.prototype.UnboundTo = function (boundary)
284 {
285   for (var b = this; b != boundary; b = b.next)
286     b.vari.val = b.old_value;
287   return boundary;
288 }
289
290 Xex.Bind = function (bindings, vari, val)
291 {
292   var b = new Xex.Bindings (vari);
293   b.vari.val = val;
294   b.next = bindings;
295   return b;
296 }
297
298 Xex.CatchTag = {
299   Return: 0,
300   Break: 1
301 }
302
303 Xex.Domain = function (name, parent, context)
304 {
305   this.name = name;
306   this.context = context;
307   this.depth = 0;
308
309   if (name != 'basic' && ! parent)
310     parent = Xex.BasicDomain
311   this.parent = parent;
312   this.termtypes = {};
313   this.functions = {};
314   this.variables = {};
315   if (parent)
316     {
317       var elt;
318       for (elt in parent.termtypes)
319         this.termtypes[elt] = parent.termtypes[elt];
320       for (elt in parent.functions)
321         this.functions[elt] = parent.functions[elt];
322       for (elt in parent.variables)
323         {
324           var vari = parent.variables[elt];
325           this.variables[elt] = new Xex.Variable (vari.name, vari.desc,
326                                                   vari.val, vari.range);
327         }
328     }
329
330   this.call_stack = new Array ();
331   this.bindings = null;
332   this.catch_stack = new Array ();
333   this.catch_count = 0;
334   this.caught = false;
335 };
336
337 Xex.Domain.prototype = {
338   CallStackCount: function () { return this.call_stack.length; },
339   CallStackPush: function (term) { this.call_stack.push (term); },
340   CallStackPop: function () { this.call_stack.pop (); },
341   Bind: function (vari, val)
342   {
343     this.bindings = Xex.Bind (this.bindings, vari, val);
344   },
345   UnboundTo: function (boundary)
346   {
347     if (this.bindings)
348       this.bindings = this.bindings.UnboundTo (boundary);
349   },
350   Catch: function (tag) { this.catch_stack.push (tag); this.catch_count++; },
351   Uncatch: function ()
352   {
353     this.catch_stack.pop ();
354     if (this.catch_count > this.catch_stack.length)
355       this.catch_count--;
356   },
357   Thrown: function ()
358   {
359     if (this.catch_count < this.catch_stack.length)
360       {
361         this.caught = (this.catch_count == this.catch_stack.length - 1);
362         return true;
363       }
364     this.caught = false;
365     return false;
366   },
367   ThrowReturn: function ()
368   {
369     for (var i = this.catch_stack.length - 1; i >= 0; i--)
370       {
371         this.catch_count--;
372         if (this.catch_stack[i] == Xex.CatchTag.Return)
373           break;
374       }
375   },
376   ThrowBreak: function ()
377   {
378     if (this.catch_stack[this.catch_stack.length - 1] != Xex.CatchTag.Break)
379       throw new Xex.ErrTerm (Xex.Error.NoLoopToBreak,
380                            "No surrounding loop to break");
381     this.catch_count--;
382   },
383   ThrowSymbol: function (tag)
384   {
385     var i = this.catch_count;
386     for (var j = this.catch_stack.length - 1; j >= 0; j--)
387       {
388         i--;
389         if (Xex.CatchTag.Matches (this.catch_stack[i], tag))
390           {
391             this.catch_count = i;
392             return;
393           }
394       }
395     throw new Xex.ErrTerm (Xex.Error.UncaughtThrow,
396                          "No corresponding catch: " + tag);
397   },
398   DefType: function (obj)
399   {
400     var type = obj.type;
401     if (this.termtypes[type])
402       throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
403                            "Already defined: " + type);
404     if (this.functions[type])
405       throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
406                            "Already defined as a funciton or a macro: "
407                            + type);
408     this.termtypes[type] = obj.Parser;
409   },
410   DefSubr: function (builtin, name, with_var, min_args, max_args)
411   {
412     this.functions[name] = new Xex.Subrountine (builtin, name, with_var,
413                                                 min_args, max_args);
414   },
415   DefSpecial: function (builtin, name, with_var, min_args, max_args)
416   {
417     this.functions[name] = new Xex.SpecialForm (builtin, name, with_var,
418                                                 min_args, max_args);
419   },
420   Defun: function (name, min_args, max_args, args, body)
421   {
422     this.functions[name] =  new Xex.Lambda (name, min_args, max_args,
423                                             args, body);
424   },
425   DefunByFunc: function (func) { this.functions[func.name] = func; },
426   Defmacro: function (name, min_args, max_args, args, body)
427   {
428     this.functions[name] = new Xex.Macro (name, min_args, max_args,
429                                           args, body);
430   },
431   DefAlias: function (alias, fname)
432   {
433     var func = this.functions[fname];
434
435     if (! func)
436       throw new Xex.ErrTerm (Xex.Error.UnknownFunction, fname);
437     if (this.termtypes[alias])
438       throw new Xex.ErrTerm (Xex.Error.FunctionConflict,
439                            "Already defined as a term type: " + alias);
440     if (this.functions[alias])
441       throw new Xex.ErrTerm (Xex.Error.FunctionConflict,
442                            "Already defined as a function: " + alias);
443     this.functions[alias] = func;
444   },
445   Defvar: function (name, desc, val, range)
446   {
447     var vari = new Xex.Variable (name, desc, val, range);
448     this.variables[name] = vari;
449     return vari;
450   },
451   GetFunc: function (name)
452   {
453     var func = this.functions[name];
454     if (! func)
455       throw new Xex.ErrTerm (Xex.Error.UnknownFunction,
456                              "Unknown function: " + name);
457     return func;
458   },
459   CopyFunc: function (domain, name)
460   {
461     var func = this.functions[name];
462     domain.DefunByFunc (func);
463     return true;
464   },
465   CopyFuncAll: function (domain)
466   {
467     for (var elt in this.functions)
468       domain.DefunByFunc (this.functions[elt]);
469   },
470   GetVarCreate: function (name)
471   {
472     var vari = this.variables[name];
473     if (! vari)
474       vari = this.variables[name] = new Xex.Variable (name, null,
475                                                       Xex.Zero, null);
476     return vari;
477   },
478   GetVar: function (name) { return this.variables[name]; },
479   SaveValues: function ()
480   {
481     values = {};
482     for (var elt in this.variables)
483       values[elt] = this.variables[elt].val.Clone ();
484     return values;
485   },
486   RestoreValues: function (values)
487   {
488     var name;
489     for (name in values)
490       {
491         var vari = this.variables[name];
492         vari.val = values[name];
493       }
494   }
495 };
496
497 Xex.Term = function (type) { this.type = type; }
498 Xex.Term.prototype = {
499   IsTrue: function () { return true; },
500   Eval: function (domain) { return this.Clone (); },
501   Clone: function (domain) { return this; },
502   equals: function (obj)
503   {
504     return (this.type == obj.type
505             && this.val != undefined
506             && obj.val == this.val);
507   },
508   Matches: function (obj) { return this.equals (obj); },
509   toString: function ()
510   {
511     if (this.val != undefined)
512       return '<' + this.type + '>' + this.val + '</' + this.type + '>';
513     return '<' + this.type + '/>';
514   },
515   Intval: function () { throw new Xex.ErrTerm (Xex.Error.WrongType,
516                                                "Not an integer"); },
517   Strval: function () { throw new Xex.ErrTerm (Xex.Error.WrongType,
518                                                "Not a string"); }
519 };
520
521 Node.prototype.firstElement = function ()
522 {
523   for (var n = this.firstChild; n; n = n.nextSibling)
524     if (n.nodeType == 1)
525       return n;
526   return null;
527 }
528
529 Node.prototype.nextElement = function ()
530 {
531   for (var n = this.nextSibling; n; n = n.nextSibling)
532     if (n.nodeType == 1)
533       return n;
534   return null;
535 };
536
537 (function () {
538   function parse_defvar (domain, node)
539   {
540     var name = node.attributes['vname'].nodeValue;
541     if (! name)
542       throw new Xex.ErrTerm (Xex.Error.NoVariableName, node, '');
543     var vari = domain.variables[name];
544     var desc, val = null, range;
545     if (vari)
546       {
547         desc = vari.description;
548         val = vari.val;
549         range = vari.range;
550       }
551     node = node.firstElement ();
552     if (node && node.nodeName == 'description')
553       {
554         desc = node.firstChild.nodeValue;
555         node = node.nextElement ();
556       }
557     if (node)
558       {
559         val = Xex.Term.Parse (domain, node);
560         node = node.nextElement ();
561         if (node && node.nodeName == 'possible-values')
562           for (node = node.firstElement (); node; node = node.nextElement ())
563             {
564               var pval;
565               if (node.nodeName == 'range')
566                 {
567                   if (! val.IsInt)
568                     throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
569                                            'Range not allowed for ' + name);
570                   pval = new Array ();
571                   for (var n = node.firstElement (); n; n = n.nextElement ())
572                     {
573                       var v = Xex.Term.Parse (domain, n);
574                       if (! v.IsInt)
575                         throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
576                                                'Invalid range value: ' + val);
577                       pval.push (v);
578                     }
579                   }
580               else
581                 {
582                   pval = Xex.Term.Parse (domain, node);
583                   if (val.type != pval.type)
584                     throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid,
585                                            'Invalid possible value: ' + pval);
586                 }
587               if (! range)
588                 range = new Array ();
589               range.push (pval);
590           }
591       }
592     if (val == null)
593       val = Xex.Zero;
594     domain.Defvar (name, desc, val, range);
595     return name;
596   }
597
598   function parse_defun_head (domain, node)
599   {
600     var name = node.attributes['fname'].nodeValue;
601     if (! name)
602       throw new Xex.ErrTerm (Xex.Error.NoFunctionName, node, '');
603     var args = new Array ();
604     var nfixed = 0, noptional = 0, nrest = 0;
605
606     node = node.firstElement ();
607     if (node && node.nodeName == 'args')
608       {
609         var n;
610         for (n = n.firstElement (); n; n = n.nextElement ())
611           {
612             if (n.nodeName == 'fixed')
613               nfixed++;
614             else if (n.nodeName == 'optional')
615               noptional++;
616             else if (n.nodeName == 'rest')
617               nrest++;
618             else
619               throw new Xex.ErrTerm (Xex.Error.WrongType, n, n.nodeName);
620           }
621         if (nrest > 1)
622           throw new Xex.ErrTerm (Xex.Error.WrongType, n, 'Too many <rest>');
623         for (n = node.firstElement (); n; n = n.nextElement ())
624           args.push (domain.DefVar (n.attributes['vname'].nodeValue));
625       }
626     args.min_args = nfixed;
627     args.max_args = nrest == 0 ? nfixed + noptional : -1;
628
629     if (node.nodeName == 'defun')
630       domain.Defun (name, args, null);
631     else
632       domain.Defmacro (name, args, null);
633     return name;
634   }
635
636   function parse_defun_body (domain, node)
637   {
638     var name = node.attributes['fname'].nodeValue;
639     var func = domain.GetFunc (name);
640     var body;
641     for (node = node.firstElement (); node; node = node.nextElement ())
642       if (node.nodeName != 'description' && node.nodeName != 'args')
643         break;
644     body = Xex.Term.Parse (domain, node, null);
645     func.body = body;
646   }
647
648   Xex.Term.Parse = function (domain, node, stop)
649   {
650     if (arguments.length == 2)
651       {
652         var name = node.nodeName;
653         var parser = domain.termtypes[name];
654
655         if (parser)
656           return parser (domain, node);
657         if (name == 'defun' || name == 'defmacro')
658           {
659             name = parse_defun_head (domain, node);
660             parse_defun_body (domain, node);
661             return new Xex.StrTerm (name);
662           }
663         if (name == 'defvar')
664           {
665             name = parse_defvar (domain, node);
666             return new Xex.StrTerm (name);
667           }
668         return new Xex.Funcall.prototype.Parser (domain, node);
669       }
670     for (var n = node; n && n != stop; n = n.nextElement ())
671       if (n.nodeName == 'defun' || n.nodeName == 'defmacro')
672         parse_defun_head (domain, n);
673     var terms = null;
674     for (var n = node; n && n != stop; n = n.nextElement ())
675       {
676         if (n.nodeName == 'defun' || n.nodeName == 'defmacro')
677           parse_defun_body (domain, n);
678         else if (n.nodeName == 'defvar')
679           parse_defvar (domain, n);
680         else
681           {
682             if (! terms)
683               terms = new Array ();
684             terms.push (Xex.Term.Parse (domain, n));
685           }
686       }
687     return terms;
688   }
689 }) ();
690
691 Xex.Varref = function (vname)
692 {
693   this.val = vname;
694 };
695
696 (function () {
697   var proto = new Xex.Term ('varref');
698
699   proto.Clone = function () { return new Xex.Varref (this.val); }
700   proto.Eval = function (domain)
701   {
702     var vari = domain.GetVarCreate (this.val);
703     Xex.Log (this.ToString () + '=>' + vari.val, domain.depth);
704     return vari.val;
705   }
706
707   proto.Parser = function (domain, node)
708   {
709     return new Xex.Varref (node.attributes['vname'].nodeValue);
710   }
711
712   proto.ToString = function ()
713   {
714     return '<varref vname="' + this.val + '"/>';
715   }
716
717   Xex.Varref.prototype = proto;
718 }) ();
719
720 var null_args = new Array ();
721   
722 Xex.Funcall = function (func, vname, args)
723 {
724   this.func = func;
725   this.vname = vname;
726   this.args = args || null_args;
727 };
728
729 (function () {
730   var proto = new Xex.Term ('funcall');
731
732   proto.Parser = function (domain, node)
733   {
734     var fname = node.nodeName;
735     var attr;
736
737     if (fname == 'funcall')
738       fname = node.attributes['fname'].nodeValue;
739     var func = domain.GetFunc (fname);
740     var vname;
741     attr = node.attributes['vname'];
742     vname = attr != undefined ? attr.nodeValue : null;
743     var args = Xex.Term.Parse (domain, node.firstElement (), null);
744     return new Xex.Funcall (func, vname, args);
745   }
746
747   proto.New = function (domain, fname, vname, args)
748   {
749     var func = domain.GetFunc (fname);
750     var funcall = new Xex.Funcall (func, vname, args);
751     if (func instanceof Xex.Macro)
752       funcall = funcall.Eval (domain);
753     return funcall;
754   }
755
756   proto.Eval = function (domain)
757   {
758     Xex.Log (this, domain.depth);
759     var vari;
760     if (this.vname)
761       vari = domain.GetVarCreate (this.vname);
762     domain.depth++;
763     var result;
764     try {
765       result = this.func.Call (domain, vari, this.args);
766     } finally {
767       Xex.Log (' => ' + result, --domain.depth,
768                this.func instanceof Xex.Subrountine);
769     }
770     return result;
771   }
772
773   proto.Clone = function ()
774   {
775     return new Xex.Funcall (this.func, this.vari, this.args);
776   }
777
778   proto.equals = function (obj)
779   {
780     return (obj.type == 'funcall'
781             && obj.func == this.func
782             && obj.vari.equals (this.vari)
783             && obj.args.length == this.func.length);
784   }
785
786   proto.toString = function ()
787   {
788     var arglist = ''
789     var len = this.args.length;
790     var str = '<' + this.func.name;
791     if (this.vname)
792       str += ' vname="' + this.vname + '"';
793     if (len == 0)
794       return str + '/>';
795     if (this.func instanceof Xex.Subrountine)
796       for (var i = 0; i < len; i++)
797         arglist += this.args[i].toString ();
798     else
799       for (var i = 0; i < len; i++)
800         arglist += '.';
801     return str + '>' + arglist + '</' + this.func.name + '>';
802   }
803
804   Xex.Funcall.prototype = proto;
805 }) ();
806
807 Xex.ErrTerm = function (ename, message, stack)
808 {
809   this.ename = ename;
810   this.message = message;
811   this.stack = stack;
812 };
813
814 (function () {
815   var proto = new Xex.Term ('error');
816
817   proto.IsError = true;
818
819   proto.Parser = function (domain, node)
820   {
821     return new Xex.ErrTerm (node.attributes['ename'].nodeValue,
822                             node.innerText, false);
823   }
824
825   proto.CallStack = function () { return stack; }
826
827   proto.SetCallStack = function (value) { statck = value; }
828
829   proto.Clone = function ()
830   {
831     return new Xex.ErrTerm (ename, message, false);
832   }
833
834   proto.equals = function (obj)
835   {
836     return (obj.IsError
837             && obj.ename == ename && obj.message == message
838             && (obj.stack ? (stack && stack.length == obj.stack.length)
839                 : ! stack));
840   }
841
842   proto.Matches = function (obj)
843   {
844     return (obj.IsError && obj.ename == ename);
845   }
846
847   proto.toString = function ()
848   {
849     return '<error ename="' + this.ename + '">' + this.message + '</error>';
850   }
851
852   Xex.ErrTerm.prototype = proto;
853 }) ();
854
855 Xex.IntTerm = function (num) { this.val = num; };
856 (function () {
857   var proto = new Xex.Term ('integer');
858   proto.IsInt = true;
859   proto.Intval = function () { return this.val; };
860   proto.IsTrue = function () { return this.val != 0; }
861   proto.Clone = function () { return new Xex.IntTerm (this.val); }
862   proto.Parser = function (domain, node)
863   {
864     var str = node.firstChild.nodeValue;
865
866     if (str.charAt (0) == '?' && str.length == 2)
867       return new Xex.IntTerm (str.charCodeAt (1));
868     return new Xex.IntTerm (parseInt (node.firstChild.nodeValue));
869   }
870   Xex.IntTerm.prototype = proto;
871 }) ();
872
873 Xex.StrTerm = function (str) { this.val = str; };
874 (function () {
875   var proto = new Xex.Term ('string');
876   proto.IsStr = true;
877   proto.Strval = function () { return this.val; };
878   proto.IsTrue = function () { return this.val.length > 0; }
879   proto.Clone = function () { return new Xex.StrTerm (this.val); }
880   proto.Parser = function (domain, node)
881   {
882     return new Xex.StrTerm (node.firstChild ? node.firstChild.nodeValue : '');
883   }
884   Xex.StrTerm.prototype = proto;
885 }) ();
886
887 Xex.SymTerm = function (str) { this.val = str; };
888 (function () {
889   var proto = new Xex.Term ('symbol');
890   proto.IsSymbol = true;
891   proto.IsTrue = function () { return this.val != 'nil'; }
892   proto.Clone = function () { return new Xex.SymTerm (this.val); }
893   proto.Parser = function (domain, node)
894   {
895     return new Xex.SymTerm (node.firstChild.nodeValue);
896   }
897   Xex.SymTerm.prototype = proto;
898 }) ();
899
900 Xex.LstTerm = function (list) { this.val = list; };
901 (function () {
902   var proto = new Xex.Term ('list');
903   proto.IsList = true;
904   proto.IsTrue = function () { return this.val.length > 0; }
905   proto.Clone = function () { return new Xex.LstTerm (this.val.slice (0)); }
906
907   proto.equals = function (obj)
908   {
909     if (obj.type != 'list' || obj.val.length != this.val.length)
910       return false;
911     var i, len = this.val.length;
912     for (i = 0; i < len; i++)
913       if (! this.val[i].equals (obj.val[i]))
914         return false;
915     return true;
916   }
917
918   proto.Parser = function (domain, node)
919   {
920     var list = Xex.Term.Parse (domain, node.firstElement (), null);
921     return new Xex.LstTerm (list);
922   }
923
924   proto.toString = function ()
925   {
926     var len = this.val.length;
927
928     if (len == 0)
929       return '<list/>';
930     var str = '<list>';
931     for (var i = 0; i < len; i++)
932       str += this.val[i].toString ();
933     return str + '</list>';
934   }
935   Xex.LstTerm.prototype = proto;
936 }) ();
937
938 (function () {
939   var basic = new Xex.Domain ('basic', null, null);
940
941   function Fset (domain, vari, args)
942   {
943     if (! vari)
944       throw new Xex.ErrTerm (Xex.Error.NoVariableName,
945                              'No variable name to set');
946     vari.SetValue (args[0]);
947     return args[0];
948   }
949
950   function Fnot (domain, vari, args)
951   {
952     return (args[0].IsTrue () ? Xex.Zero : Xex.One);
953   }
954
955   function maybe_set_intvar (vari, n)
956   {
957     var term = new Xex.IntTerm (n);
958     if (vari)
959       vari.SetValue (term);
960     return term;
961   }
962
963   function Fadd (domain, vari, args)
964   {
965     var n = vari ? vari.val.Intval () : 0;
966     var len = args.length;
967
968     for (var i = 0; i < len; i++)
969       n += args[i].Intval ();
970     return maybe_set_intvar (vari, n);
971   }
972
973   function Fmul (domain, vari, args)
974   {
975     var n = vari ? vari.val.Intval () : 1;
976     for (var i = 0; i < args.length; i++)
977       n *= args[i].Intval ();
978     return maybe_set_intvar (vari, n);
979   }
980
981   function Fsub (domain, vari, args)
982   {
983     var n, i;
984
985     if (! vari)
986       {
987         n = args[0].Intval ();
988         i = 1;
989       }
990     else
991       {
992         n = vari.val.Intval ();
993         i = 0;
994       }
995     while (i < args.length)
996       n -= args[i++].Intval ();
997     return maybe_set_intvar (vari, n);
998   }
999
1000   function Fdiv (domain, vari, args)
1001   {
1002     var n, i;
1003
1004     if (! vari == null)
1005       {
1006         n = args[0].Intval ();
1007         i = 1;
1008       }
1009     else
1010       {
1011         n = vari.val.Intval ();
1012         i = 0;
1013       }
1014     while (i < args.length)
1015       n /= args[i++].Intval ();
1016     return maybe_set_intvar (vari, n);
1017   }
1018
1019   function Fmod (domain, vari, args)
1020   {
1021     return maybe_set_intvar (vari, args[0].Intval () % args[1].Intval ());
1022   }
1023
1024   function Flogior (domain, vari, args)
1025   {
1026     var n = vari == null ? 0 : vari.val;
1027     for (var i = 0; i < args.length; i++)
1028       n |= args[i].val;
1029     return maybe_set_intvar (vari, n);
1030   }
1031
1032   function Flogand (domain, vari, args)
1033   {
1034     var n, i;
1035     if (vari == null)
1036       {
1037         Xex.Log ("logand arg args[0]" + args[0]);
1038         n = args[0].Intval ()
1039         i = 1;
1040       }
1041     else
1042       {
1043         Xex.Log ("logand arg var " + vari);
1044         n = vari.val.Intval ();
1045         i = 0;
1046       }
1047     while (n > 0 && i < args.length)
1048       {
1049         Xex.Log ("logand arg " + args[i]);
1050         n &= args[i++].val;
1051       }
1052     return maybe_set_intvar (vari, n);
1053   }
1054
1055   function Flsh (domain, vari, args)
1056   {
1057     return maybe_set_intvar (vari, args[0].Intval () << args[1].Intval ());
1058   }
1059
1060   function Frsh (domain, vari, args)
1061   {
1062     return maybe_set_intvar (vari, args[0].Intval () >> args[1].Intval ());
1063   }
1064
1065   function Fand (domain, vari, args)
1066   {
1067     var len = args.length;
1068     for (var i = 0; i < len; i++)
1069     {
1070       var result = args[i].Eval (domain);
1071       if (domain.Thrown ())
1072         return result;
1073       if (! result.IsTrue ())
1074         return Xex.Zero;
1075     }
1076     return Xex.One;
1077   }
1078
1079   function For (domain, vari, args)
1080   {
1081     var len = args.length;
1082     for (var i = 0; i < len; i++)
1083     {
1084       var result = args[i].Eval (domain);
1085       if (domain.Thrown ())
1086         return result;
1087       if (result.IsTrue ())
1088         return Xex.One;
1089     }
1090     return Xex.Zero;
1091   }
1092
1093   function Feq (domain, vari, args)
1094   {
1095     for (var i = 1; i < args.length; i++)
1096       if (! args[i - 1].equals (args[i]))
1097         return Xex.Zero;
1098     return Xex.One;
1099   }
1100
1101   function Fnoteq (domain, vari, args)
1102   {
1103     return (Feq (domain, vari, args) == Xex.One ? Xex.Zero : Xex.One);
1104   }
1105
1106   function Flt (domain, vari, args)
1107   {
1108     var n = args[0].Intval ();
1109
1110     for (var i = 1; i < args.length; i++)
1111       {
1112         var n1 = args[i].Intval ();
1113         if (n >= n1)
1114           return Xex.Zero;
1115         n = n1;
1116       }
1117     return Xex.One;
1118   }
1119
1120   function Fle (domain, vari, args)
1121   {
1122     var n = args[0].Intval ();
1123     for (var i = 1; i < args.length; i++)
1124       {
1125         var n1 = args[i].Intval ();
1126         if (n > n1)
1127           return Xex.Zero;
1128         n = n1;
1129       }
1130     return Xex.One;
1131   }
1132
1133   function Fgt (domain, vari, args)
1134   {
1135     var n = args[0].Intval ();
1136     for (var i = 1; i < args.length; i++)
1137       {
1138         var n1 = args[i].Intval ();
1139         if (n <= n1)
1140           return Xex.Zero;
1141         n = n1;
1142       }
1143     return Xex.One;
1144   }
1145
1146   function Fge (domain, vari, args)
1147   {
1148     var n = args[0].Intval ();
1149     for (var i = 1; i < args.length; i++)
1150       {
1151         var n1 = args[i].Intval ();
1152         if (n < n1)
1153           return Xex.Zero;
1154         n = n1;
1155       }
1156     return Xex.One;
1157   }
1158
1159   function Fprogn (domain, vari, args)
1160   {
1161     var result = Xex.One;
1162     var len = args.length;
1163
1164     for (var i = 0; i < len; i++)
1165       {
1166         result = args[i].Eval (domain);
1167         if (domain.Thrown ())
1168           return result;
1169       }
1170     return result;
1171   }
1172
1173   function Fif (domain, vari, args)
1174   {
1175     var result = args[0].Eval (domain);
1176
1177     if (domain.Thrown ())
1178       return result;
1179     if (result.IsTrue ())
1180       return args[1].Eval (domain);
1181     if (args.length == 2)
1182       return Xex.Zero;
1183     return args[2].Eval (domain);
1184   }
1185
1186   function Fcond (domain, vari, args)
1187   {
1188     for (var i = 0; i < args.length; i++)
1189       {
1190         var list = args[i];
1191         var result = list.val[0].Eval (domain);
1192         if (result.IsTrue ())
1193           {
1194             for (var j = 1; j < list.val.length; j++)
1195               {
1196                 domain.depth++;
1197                 result = list.val[j].Eval (domain);
1198                 domain.depth--;
1199                 if (domain.Thrown ())
1200                   return result;
1201                 }
1202             return result;
1203           }
1204       }
1205     return Xex.Zero;
1206   }
1207
1208   function eval_terms (domain, terms, idx)
1209   {
1210     var result = Xex.Zero;
1211     domain.caught = false;
1212     for (var i = idx; i < terms.length; i++)
1213       {
1214         result = terms[i].Eval (domain);
1215         if (domain.Thrown ())
1216           return result;
1217       }
1218     return result;
1219   }
1220
1221   function Fcatch (domain, vari, args)
1222   {
1223     var caught = false;
1224     var result;
1225
1226     if (args[0].IsError)
1227       {
1228         try {
1229           result = eval_terms (domain, args, 1);
1230         } catch (e) {
1231           if (e instanceof Xex.ErrTerm)
1232             {
1233               if (! args[0].Matches (e))
1234                 throw e;
1235               if (vari)
1236                 vari.SetValue (e);
1237               return Xex.One;
1238             }
1239         }
1240       }
1241     else if (args[0].IsSymbol)
1242       {
1243         try {
1244           domain.Catch (args[0].val);
1245           result = eval_terms (domain, args, 1);
1246           if (domain.caught)
1247             {
1248               if (vari != null)
1249                 vari.SetValue (result);
1250               return Xex.One;
1251             }
1252           return Xex.Zero;
1253         } finally {
1254           domain.Uncatch ();
1255         }
1256       }
1257     throw new Xex.ErrTerm (Xex.Error.WrongArgument,
1258                            "Not a symbol nor an error: " + args[0]);
1259   }
1260
1261   function Fthrow (domain, vari, args)
1262   {
1263     if (args[0].IsSymbl)
1264       {
1265         domain.ThrowSymbol (args[0]);
1266         return (args[args.length - 1]);
1267       }
1268     if (args[0].IsError)
1269       {
1270         throw args[0];
1271       }
1272     throw new Xex.ErrTerm (Xex.Error.WrongArgument,
1273                            "Not a symbol nor an error:" + args[0]);
1274   }
1275
1276   Xex.BasicDomain = basic;
1277
1278   basic.DefSubr (Fset, "set", true, 1, 1);
1279   basic.DefAlias ("=", "set");
1280   basic.DefSubr (Fnot, "not", false, 1, 1);
1281   basic.DefAlias ("!", "not");
1282   basic.DefSubr (Fadd, "add", true, 1, -1);
1283   basic.DefSubr (Fmul, "mul", true, 1, -1);
1284   basic.DefAlias ("*", "mul");
1285   basic.DefSubr (Fsub, "sub", true, 1, -1);
1286   basic.DefAlias ("-", "sub");
1287   basic.DefSubr (Fdiv, "div", true, 1, -1);
1288   basic.DefAlias ("/", "div");
1289   basic.DefSubr (Fmod, "mod", true, 1, 2);
1290   basic.DefAlias ("%", "mod");
1291   basic.DefSubr (Flogior, "logior", true, 1, -1);
1292   basic.DefAlias ('|', "logior");
1293   basic.DefSubr (Flogand, "logand", true, 1, -1);
1294   basic.DefAlias ("&", "logand");
1295   basic.DefSubr (Flsh, "lsh", true, 1, 2);
1296   basic.DefAlias ("<<", "lsh");
1297   basic.DefSubr (Frsh, "rsh", true, 1, 2);
1298   basic.DefAlias (">>", "rsh");
1299   basic.DefSubr (Feq, "eq", false, 2, -1);
1300   basic.DefAlias ("==", "eq");
1301   basic.DefSubr (Fnoteq, "noteq", false, 2, 2);
1302   basic.DefAlias ("!=", "noteq");
1303   basic.DefSubr (Flt, "lt", false, 2, -1);
1304   basic.DefAlias ("<", "lt");
1305   basic.DefSubr (Fle, "le", false, 2, -1);
1306   basic.DefAlias ("<=", "le");
1307   basic.DefSubr (Fgt, "gt", false, 2, -1);
1308   basic.DefAlias (">", "gt");
1309   basic.DefSubr (Fge, "ge", false, 2, -1);
1310   basic.DefAlias (">=", "ge");
1311   basic.DefSubr (Fthrow, "throw", false, 1, 2);
1312
1313   //basic.DefSubr (Fappend, "append", true, 0, -1);
1314   //basic.DefSubr (Fconcat, "concat", true, 0, -1);
1315   //basic.DefSubr (Fnth, "nth", false, 2, 2);
1316   //basic.DefSubr (Fcopy, "copy", false, 1, 1);
1317   //basic.DefSubr (Fins, "ins", true, 2, 2);
1318   //basic.DefSubr (Fdel, "del", true, 2, 2);
1319   //basic.DefSubr (Feval, "eval", false, 1, 1);
1320   //basic.DefSubr (Fbreak, "break", false, 0, 1);
1321   //basic.DefSubr (Freturn, "return", false, 0, 1);
1322   //basic.DefSubr (Fthrow, "throw", false, 1, 2);
1323
1324   basic.DefSpecial (Fand, "and", false, 1, -1);
1325   basic.DefAlias ("&&", "and");
1326   basic.DefSpecial (For, "or", false, 1, -1);
1327   basic.DefAlias ("||", "or");
1328   basic.DefSpecial (Fprogn, "progn", false, 1, -1);
1329   basic.DefAlias ("expr", "progn");
1330   basic.DefSpecial (Fif, "if", false, 2, 3);
1331   //basic.DefSpecial (Fwhen, "when", false, 1, -1);
1332   //basic.DefSpecial (Floop, "loop", false, 1, -1);
1333   //basic.DefSpecial (Fwhile, "while", false, 1, -1);
1334   basic.DefSpecial (Fcond, "cond", false, 1, -1);
1335   //basic.DefSpecial (Fforeach, "foreach", true, 2, -1);
1336   //basic.DefSpecial (Fquote, "quote", false, 1, 1);
1337   //basic.DefSpecial (Ftype, "type", false, 1, 1);
1338   basic.DefSpecial (Fcatch, "catch", true, 2, -1);
1339
1340   basic.DefType (Xex.Funcall.prototype);
1341   basic.DefType (Xex.Varref.prototype);
1342   basic.DefType (Xex.ErrTerm.prototype);
1343   basic.DefType (Xex.IntTerm.prototype);
1344   basic.DefType (Xex.StrTerm.prototype);
1345   basic.DefType (Xex.SymTerm.prototype);
1346   basic.DefType (Xex.LstTerm.prototype);
1347
1348 }) ();
1349
1350 Xex.Zero = new Xex.IntTerm (0);
1351 Xex.One = new Xex.IntTerm (1);
1352 Xex.nil = new Xex.SymTerm ('nil');
1353
1354 Xex.LoadOld = function (server, file)
1355 {
1356   var obj = new XMLHttpRequest ();
1357   var url = server ? server + '/' + file : file;
1358   obj.open ('GET', url, false);
1359   obj.overrideMimeType ('text/xml');
1360   obj.send ('');
1361   return (obj.responseXML && obj.responseXML.firstChild);
1362 };
1363
1364 (function () {
1365   var queue = new Array ();
1366   var iframe = null;
1367
1368   function receiver (event)
1369   {
1370     var request = queue.shift ();
1371     //alert ('received: ' + request[0]);
1372     var parser = new DOMParser ();
1373     var xml = parser.parseFromString (event.data, 'text/xml');
1374     if (queue.length > 0)
1375       {
1376         document.getElementsByTagName ('body')[0].removeChild (iframe);
1377         iframe.src = queue[0][0];
1378         document.getElementsByTagName ('body')[0].appendChild (iframe);
1379       }
1380     else
1381       {
1382         window.removeEventListener ('message', receiver, false);
1383         document.getElementsByTagName ('body')[0].removeChild (iframe);
1384       }
1385     request[1] (xml.firstElement (), request[2]);
1386     event.preventDefault ();
1387   };
1388
1389   Xex.Load = function (server, file, callback, arg)
1390   {
1391     var url = server + '/loadxml.html#' + file;
1392     //alert ('loading file:' + file);
1393     queue.push ([url, callback, arg]);
1394     if (queue.length == 1)
1395       {
1396         window.addEventListener ('message', receiver, false);
1397         iframe = document.createElement ('iframe');
1398         iframe.style.display = 'none';
1399         iframe.src = url;
1400         //alert ('iframe created');
1401         document.getElementsByTagName ('body')[0].appendChild (iframe);
1402       }
1403   }
1404 }) ();
1405